Skip to content

Commit

Permalink
Merge pull request #33 from gamgyul-code/feat/32-recommend-route-crea…
Browse files Browse the repository at this point in the history
…te-and-find

추천 루트 생성/메인 화면 추천 루트 조회 기능 구현
  • Loading branch information
gywns0417 authored Nov 11, 2024
2 parents 30b6d34 + bcfc73c commit 05273de
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,20 @@
import com.gamgyul_code.halmang_vision.member.domain.Member;
import com.gamgyul_code.halmang_vision.member.domain.MemberRepository;
import com.gamgyul_code.halmang_vision.member.dto.ApiMember;
import com.gamgyul_code.halmang_vision.route.domain.RecommendRoute;
import com.gamgyul_code.halmang_vision.route.domain.RecommendRouteRepository;
import com.gamgyul_code.halmang_vision.route.domain.Route;
import com.gamgyul_code.halmang_vision.route.domain.RouteRepository;
import com.gamgyul_code.halmang_vision.route.domain.RouteSpot;
import com.gamgyul_code.halmang_vision.route.dto.RouteDto.CreateRecommendRouteRequest;
import com.gamgyul_code.halmang_vision.route.dto.RouteDto.CreateRouteNameUpdateRequest;
import com.gamgyul_code.halmang_vision.route.dto.RouteDto.CreateRouteRequest;
import com.gamgyul_code.halmang_vision.route.dto.RouteDto.CreateRouteSpotRequest;
import com.gamgyul_code.halmang_vision.route.dto.RouteDto.CreateRouteSpotUpdateRequest;
import com.gamgyul_code.halmang_vision.route.dto.RouteDto.MyRouteDetailResponse;
import com.gamgyul_code.halmang_vision.route.dto.RouteDto.MyRouteResponse;
import com.gamgyul_code.halmang_vision.route.dto.RouteDto.RouteResponse;
import com.gamgyul_code.halmang_vision.route.dto.RouteDto.RouteSpotResponse;
import com.gamgyul_code.halmang_vision.spot.domain.LanguageCode;
import com.gamgyul_code.halmang_vision.spot.domain.Spot;
import com.gamgyul_code.halmang_vision.spot.domain.SpotRepository;
import com.gamgyul_code.halmang_vision.spot.domain.SpotTranslation;
Expand All @@ -36,6 +40,7 @@ public class RouteService {
private final MemberRepository memberRepository;
private final SpotRepository spotRepository;
private final RouteRepository routeRepository;
private final RecommendRouteRepository recommendRouteRepository;
private final SpotTranslationRepository spotTranslationRepository;

private static final int MINIMUM_ROUTE_SPOT_SIZE = 2;
Expand All @@ -48,7 +53,7 @@ public void createRoute(CreateRouteRequest createRouteRequest, ApiMember apiMemb
List<Long> spotIds = createRouteRequest.getRouteSpots();
List<Spot> spots = spotRepository.findAllById(spotIds);

validateRouteName(routeName, member);
validateRouteName(routeName, member, false);
validateRouteSpot(spotIds);

Route route = createRouteRequest.toEntity(member);
Expand All @@ -61,13 +66,33 @@ public void createRoute(CreateRouteRequest createRouteRequest, ApiMember apiMemb
route.initRouteSpots(routeSpots);
}

public List<MyRouteResponse> findAllMyRoutes(ApiMember apiMember) {
public void createRecommendRoute(CreateRecommendRouteRequest createRecommendRouteRequest, ApiMember apiMember) {
Member member = apiMember.toMember(memberRepository);

String routeName = createRecommendRouteRequest.getRouteName();
List<Long> spotIds = createRecommendRouteRequest.getRouteSpots();
List<Spot> spots = spotRepository.findAllById(spotIds);

validateRouteName(routeName, member, true);
validateRouteSpot(spotIds);

RecommendRoute recommendRoute = createRecommendRouteRequest.toEntity(member);
recommendRouteRepository.save(recommendRoute);

List<RouteSpot> routeSpots = spots.stream()
.map(spot -> CreateRouteSpotRequest.toEntity(spot, recommendRoute))
.toList();

recommendRoute.initRouteSpots(routeSpots);
}

public List<RouteResponse> findAllMyRoutes(ApiMember apiMember) {
Member member = apiMember.toMember(memberRepository);

List<Route> routes = routeRepository.findAllByMember(member);

return routes.stream()
.map(MyRouteResponse::fromEntity)
.map(RouteResponse::fromEntity)
.toList();
}

Expand All @@ -86,6 +111,17 @@ public MyRouteDetailResponse findRouteDetail(Long routeId, ApiMember apiMember)
return MyRouteDetailResponse.fromEntity(route, routeSpotResponses);
}

public List<RouteResponse> findAllRecommendRoutes(ApiMember apiMember) {

Member member = apiMember.toMember(memberRepository);
LanguageCode languageCode = member.getLanguageCode();
List<RecommendRoute> recommendRoutes = recommendRouteRepository.findAllByLanguageCode(languageCode);

return recommendRoutes.stream()
.map(RouteResponse::fromEntity)
.toList();
}

private void validateRouteSpot(List<Long> spotIds) {

if (spotIds.size() < MINIMUM_ROUTE_SPOT_SIZE || spotIds.size() > MAXIMUM_ROUTE_SPOT_SIZE ) {
Expand All @@ -98,7 +134,11 @@ private void validateRouteSpot(List<Long> spotIds) {
}
}

private void validateRouteName(String routeName, Member member) {
private void validateRouteName(String routeName, Member member, boolean isRecommendRoute) {
if (isRecommendRoute && recommendRouteRepository.existsByRouteName(routeName)) {
throw new HalmangVisionException(ALREADY_EXIST_ROUTE_NAME);
}

if (routeRepository.existsByRouteNameAndMember(routeName, member)) {
throw new HalmangVisionException(ALREADY_EXIST_ROUTE_NAME);
}
Expand All @@ -110,7 +150,7 @@ public void updateRouteName(Long routeId, CreateRouteNameUpdateRequest createRou
.orElseThrow(() -> new HalmangVisionException(NOT_FOUND_ROUTE));

String routeName = createRouteNameUpdateRequest.getRouteName();
validateRouteName(routeName, member);
validateRouteName(routeName, member, false);

route.updateRouteName(routeName);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.gamgyul_code.halmang_vision.route.domain;

import com.gamgyul_code.halmang_vision.global.utils.BaseTimeEntity;
import com.gamgyul_code.halmang_vision.member.domain.Member;
import com.gamgyul_code.halmang_vision.spot.domain.LanguageCode;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.util.ArrayList;
import java.util.List;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class RecommendRoute extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Size(min = 1, max = 15)
@Column(unique = true)
private String routeName;

@ManyToOne
@JoinColumn(name = "member_id")
private Member member;

@Enumerated(value = EnumType.STRING)
@NotNull
private LanguageCode languageCode;

@OneToMany(mappedBy = "recommendRoute", cascade = CascadeType.ALL, orphanRemoval = true)
private List<RouteSpot> recommendRouteSpots = new ArrayList<>();

private String imgUrl;

public void updateRouteName(String routeName) {
this.routeName = routeName;
}

public void initRouteSpots(List<RouteSpot> routeSpots) {
this.recommendRouteSpots = routeSpots;
}

public void updateRouteSpots(List<RouteSpot> routeSpots) {
this.recommendRouteSpots.clear();
this.recommendRouteSpots.addAll(routeSpots);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.gamgyul_code.halmang_vision.route.domain;

import com.gamgyul_code.halmang_vision.spot.domain.LanguageCode;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;

public interface RecommendRouteRepository extends JpaRepository<RecommendRoute, Long> {
List<RecommendRoute> findAllByLanguageCode(LanguageCode languageCode);

boolean existsByRouteName(String routeName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Route extends BaseTimeEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,8 @@ public class RouteSpot extends BaseTimeEntity {
@ManyToOne
@JoinColumn(name = "route_id")
private Route route;

@ManyToOne
@JoinColumn(name = "recommend_route_id")
private RecommendRoute recommendRoute;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.gamgyul_code.halmang_vision.route.dto;

import com.gamgyul_code.halmang_vision.member.domain.Member;
import com.gamgyul_code.halmang_vision.route.domain.RecommendRoute;
import com.gamgyul_code.halmang_vision.route.domain.Route;
import com.gamgyul_code.halmang_vision.route.domain.RouteSpot;
import com.gamgyul_code.halmang_vision.spot.domain.LanguageCode;
import com.gamgyul_code.halmang_vision.spot.domain.Spot;
import com.gamgyul_code.halmang_vision.spot.domain.SpotTranslation;
import io.swagger.v3.oas.annotations.media.Schema;
Expand Down Expand Up @@ -34,6 +36,34 @@ public Route toEntity(Member member) {
}
}

@Data
@Builder
@AllArgsConstructor
@Schema(description = "추천 경로 생성 요청")
public static class CreateRecommendRouteRequest {

@Schema(description = "경로 이름", example = "나의 경로")
private String routeName;

@Schema(description = "경로에 포함된 관광지 Id 리스트", example = "[1, 2, 3]")
private List<Long> routeSpots;

@Schema(description = "경로 이미지 URL", example = "http://~~~.com/~~~.jpg")
private String imgUrl;

@Schema(description = "언어 코드", example = "KOR")
private LanguageCode languageCode;

public RecommendRoute toEntity(Member member) {
return RecommendRoute.builder()
.routeName(routeName)
.member(member)
.imgUrl(imgUrl)
.languageCode(languageCode)
.build();
}
}

@Data
@Builder
@AllArgsConstructor
Expand All @@ -49,6 +79,13 @@ public static RouteSpot toEntity(Spot spot, Route route) {
.route(route)
.build();
}

public static RouteSpot toEntity(Spot spot, RecommendRoute recommendRoute) {
return RouteSpot.builder()
.spot(spot)
.recommendRoute(recommendRoute)
.build();
}
}

@Data
Expand Down Expand Up @@ -78,8 +115,8 @@ public static class CreateRouteSpotUpdateRequest {
@Data
@Builder
@AllArgsConstructor
@Schema(description = "내가 만든 경로 목록 조회")
public static class MyRouteResponse {
@Schema(description = "경로 목록 조회")
public static class RouteResponse {

@Schema(description = "경로 ID", example = "1")
private Long id;
Expand All @@ -90,13 +127,21 @@ public static class MyRouteResponse {
@Schema(description = "루트 내 첫 번째 관광지의 이미지 URL", example = "http://~~~.com/~~~.jpg")
private String imgUrl;

public static MyRouteResponse fromEntity(Route route) {
return MyRouteResponse.builder()
public static RouteResponse fromEntity(Route route) {
return RouteResponse.builder()
.id(route.getId())
.routeName(route.getRouteName())
.imgUrl(route.getRouteSpots().get(0).getSpot().getImgUrl())
.build();
}

public static RouteResponse fromEntity(RecommendRoute recommendRoute) {
return RouteResponse.builder()
.id(recommendRoute.getId())
.routeName(recommendRoute.getRouteName())
.imgUrl(recommendRoute.getImgUrl())
.build();
}
}

@Data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
import com.gamgyul_code.halmang_vision.global.utils.AuthPrincipal;
import com.gamgyul_code.halmang_vision.member.dto.ApiMember;
import com.gamgyul_code.halmang_vision.route.application.RouteService;
import com.gamgyul_code.halmang_vision.route.dto.RouteDto.CreateRecommendRouteRequest;
import com.gamgyul_code.halmang_vision.route.dto.RouteDto.CreateRouteNameUpdateRequest;
import com.gamgyul_code.halmang_vision.route.dto.RouteDto.CreateRouteRequest;
import com.gamgyul_code.halmang_vision.route.dto.RouteDto.CreateRouteSpotUpdateRequest;
import com.gamgyul_code.halmang_vision.route.dto.RouteDto.MyRouteDetailResponse;
import com.gamgyul_code.halmang_vision.route.dto.RouteDto.MyRouteResponse;
import com.gamgyul_code.halmang_vision.route.dto.RouteDto.RouteResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
Expand Down Expand Up @@ -36,9 +37,15 @@ public void createRoute(@RequestBody CreateRouteRequest createRouteRequest, @Par
routeService.createRoute(createRouteRequest, apiMember);
}

@PostMapping("/recommend")
@Operation(summary = "추천 경로 생성", description = "추천 경로를 생성한다.")
public void createRecommendRoute(@RequestBody CreateRecommendRouteRequest createRecommendRouteRequest, @Parameter(hidden = true) @AuthPrincipal ApiMember apiMember) {
routeService.createRecommendRoute(createRecommendRouteRequest, apiMember);
}

@GetMapping
@Operation(summary = "내 경로 조회", description = "사용자가 만든 경로를 조회한다.")
public List<MyRouteResponse> findAllMyRoutes(@Parameter(hidden = true) @AuthPrincipal ApiMember apiMember) {
public List<RouteResponse> findAllMyRoutes(@Parameter(hidden = true) @AuthPrincipal ApiMember apiMember) {
return routeService.findAllMyRoutes(apiMember);
}

Expand All @@ -48,6 +55,12 @@ public MyRouteDetailResponse findMyRouteDetail(@PathVariable Long routeId, @Para
return routeService.findRouteDetail(routeId, apiMember);
}

@GetMapping("/recommend")
@Operation(summary = "추천 경로 조회", description = "사용자의 언어를 기반으로 추천 경로를 조회한다.")
public List<RouteResponse> findAllRecommendRoutes(@Parameter(hidden = true) @AuthPrincipal ApiMember apiMember) {
return routeService.findAllRecommendRoutes(apiMember);
}

@PutMapping("/{routeId}/name")
@Operation(summary = "내 경로 이름 수정", description = "경로를 수정한다.")
public void updateRouteName(@PathVariable Long routeId, @RequestBody CreateRouteNameUpdateRequest createRouteNameUpdateRequest, @Parameter(hidden = true) @AuthPrincipal ApiMember apiMember) {
Expand Down

0 comments on commit 05273de

Please sign in to comment.