Skip to content

Commit

Permalink
Merge pull request #84 from Route-Box/feature/#83
Browse files Browse the repository at this point in the history
구매한(담은) 루트 상세(단건)조회 API 구현
  • Loading branch information
Wo-ogie authored Sep 12, 2024
2 parents 67e95bf + a7836c5 commit eb8283d
Show file tree
Hide file tree
Showing 19 changed files with 434 additions and 125 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.routebox.routebox.application.purchased_route

import com.routebox.routebox.application.purchased_route.dto.GetPurchasedRouteCommand
import com.routebox.routebox.domain.purchased_route.PurchasedRoute
import com.routebox.routebox.domain.purchased_route.PurchasedRouteService
import com.routebox.routebox.exception.purchased_route.RouteNotPurchasedException
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Transactional

@Component
class GetPurchasedRouteUseCase(
private val purchasedRouteService: PurchasedRouteService,
) {
@Transactional(readOnly = true)
operator fun invoke(command: GetPurchasedRouteCommand): PurchasedRoute {
val purchasedRoute = purchasedRouteService.getPurchasedRouteById(command.purchasedRouteId)
if (purchasedRoute.buyer.id != command.requesterId) {
throw RouteNotPurchasedException()
}
return purchasedRoute
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.routebox.routebox.application.purchased_route.dto

data class GetPurchasedRouteCommand(
val requesterId: Long,
val purchasedRouteId: Long,
)
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,6 @@ class PurchaseRouteUseCase(

val buyer = userService.getUserById(command.buyerId)
val route = routeService.getRouteById(command.routeId)
purchasedRouteService.createPurchasedRoute(PurchasedRoute.createFrom(route, buyer))
purchasedRouteService.createPurchasedRoute(PurchasedRoute.fromRoute(route, buyer))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.routebox.routebox.controller.purchased_route

import com.routebox.routebox.application.purchased_route.GetPurchasedRouteUseCase
import com.routebox.routebox.application.purchased_route.dto.GetPurchasedRouteCommand
import com.routebox.routebox.controller.purchased_route.dto.PurchasedRouteResponse
import com.routebox.routebox.security.UserPrincipal
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.media.Content
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
import io.swagger.v3.oas.annotations.security.SecurityRequirement
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@Tag(name = "담은(구매한) 루트 관련 API")
@RequestMapping("/api")
@RestController
class PurchasedRouteController(
private val getPurchasedRouteUseCase: GetPurchasedRouteUseCase,
) {
@Operation(
summary = "구매한 루트 정보 상세조회",
description = "구매한 루트 정보를 상세조회합니다.",
security = [SecurityRequirement(name = "access-token")],
)
@ApiResponses(
ApiResponse(responseCode = "200"),
ApiResponse(responseCode = "403", description = "[3401] 내가 구매하지 않은 루트인 경우", content = [Content()]),
)
@GetMapping("/v1/purchased-routes/{purchasedRouteId}")
fun getPurchasedRoute(
@AuthenticationPrincipal userPrincipal: UserPrincipal,
@Parameter(description = "조회하고자 하는, 구매한 루트의 id") @PathVariable purchasedRouteId: Long,
): PurchasedRouteResponse {
val purchasedRoute =
getPurchasedRouteUseCase(GetPurchasedRouteCommand(requesterId = userPrincipal.userId, purchasedRouteId))
return PurchasedRouteResponse.fromPurchasedRoute(purchasedRoute)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package com.routebox.routebox.controller.purchased_route.dto

import com.routebox.routebox.domain.purchased_route.PurchasedRoute
import com.routebox.routebox.domain.purchased_route.PurchasedRouteActivity
import com.routebox.routebox.domain.purchased_route.PurchasedRoutePoint
import com.routebox.routebox.domain.user.User
import io.swagger.v3.oas.annotations.media.Schema
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime

data class PurchasedRouteResponse(
@Schema(description = "Id of purchased route")
val id: Long,

@Schema(description = "루트 작성자 정보")
val writer: UserResponse,

@Schema(description = "제목", example = "공주들이랑 함께한 경주 여행")
val name: String?,

@Schema(description = "설명", example = "이런저런 얘기들이 들어갑니다...")
val description: String?,

@Schema(description = "함께한 사람들", example = "혼자")
val whoWith: String?,

@Schema(description = "인원 수")
val numberOfPeoples: Int?,

@Schema(description = "일 수", example = "1박 2일")
val numberOfDays: String?,

@Schema(description = "루트 스타일", example = "[\"뚜벅뚜벅\"]")
val styles: List<String>,

@Schema(description = "이동수단", example = "도보")
val transportation: String?,

@Schema(description = "루트 포인트(들)")
val routePoints: List<PurchasedRoutePointResponse>,

@Schema(description = "루트 활동(들)")
val routeActivities: List<PurchasedRouteActivityResponse>,
) {
companion object {
fun fromPurchasedRoute(route: PurchasedRoute): PurchasedRouteResponse =
PurchasedRouteResponse(
id = route.id,
writer = UserResponse.fromUser(route.writer),
name = route.name,
description = route.description,
whoWith = route.whoWith,
numberOfPeoples = route.numberOfPeoples,
numberOfDays = route.numberOfDays,
styles = route.styles,
transportation = route.transportation,
routePoints = route.routePoints.map { PurchasedRoutePointResponse.fromPurchasedRoutePoint(it) },
routeActivities = route.routeActivities
.map { PurchasedRouteActivityResponse.fromPurchasedRouteActivity(it) },
)
}

data class UserResponse(
@Schema(description = "Id of user")
val id: Long,

@Schema(description = "프로필 이미지 url", example = "https://user-profile-image")
val profileImageUrl: String,

@Schema(description = "닉네임", example = "귤히어로")
val nickname: String,
) {
companion object {
fun fromUser(user: User): UserResponse = UserResponse(
id = user.id,
profileImageUrl = user.profileImageUrl,
nickname = user.nickname,
)
}
}

data class PurchasedRoutePointResponse(
@Schema(description = "Id of route point")
val routePointId: Long,

@Schema(description = "위도", example = "37.123")
val latitude: String,

@Schema(description = "경도", example = "127.123")
val longitude: String,

@Schema(description = "기록 시각")
val recordAt: LocalDateTime,
) {
companion object {
fun fromPurchasedRoutePoint(point: PurchasedRoutePoint): PurchasedRoutePointResponse =
PurchasedRoutePointResponse(
routePointId = point.routePointId,
latitude = point.latitude,
longitude = point.longitude,
recordAt = point.recordAt,
)
}
}

data class PurchasedRouteActivityResponse(
@Schema(description = "Id of route activity")
val routeActivityId: Long,

@Schema(description = "장소 이름", example = "강릉 해파랑물회")
val locationName: String,

@Schema(description = "주소", example = "강릉시 경포동 경포로")
val address: String,

@Schema(description = "위도", example = "37.123")
val latitude: String?,

@Schema(description = "경도", example = "127.123")
val longitude: String?,

@Schema(description = "방문 일자")
val visitDate: LocalDate,

@Schema(description = "방문 시작 시간")
val startTime: LocalTime,

@Schema(description = "방문 종료 시간")
val endTime: LocalTime,

@Schema(description = "카테고리", example = "관광명소")
val category: String,

@Schema(description = "설명", example = "장소에 대한 설명이 들어갑니다...")
val description: String?,

@Schema(description = "활동에 대한 이미지(들)", example = "[\"https://image-1\", \"https://image-2\"]")
val activityImageUrls: List<String>,
) {
companion object {
fun fromPurchasedRouteActivity(activity: PurchasedRouteActivity): PurchasedRouteActivityResponse =
PurchasedRouteActivityResponse(
routeActivityId = activity.routeActivityId,
locationName = activity.locationName,
address = activity.address,
latitude = activity.latitude,
longitude = activity.longitude,
category = activity.category,
visitDate = activity.visitDate,
startTime = activity.startTime,
endTime = activity.endTime,
description = activity.description,
activityImageUrls = activity.activityImageUrls,
)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package com.routebox.routebox.controller.user

import com.routebox.routebox.application.user.GetUserRouteUseCase
import com.routebox.routebox.controller.user.dto.GetPurchasedRouteResponse
import com.routebox.routebox.controller.user.dto.GetUserRoutesResponse
import com.routebox.routebox.controller.user.dto.PurchasedRouteResponse
import com.routebox.routebox.controller.user.dto.UserRouteResponse
import com.routebox.routebox.security.UserPrincipal
import io.swagger.v3.oas.annotations.Operation
Expand Down Expand Up @@ -32,50 +30,4 @@ class UserRouteController(
val routeResponse = getUserRouteUseCase(userId = principal.userId)
return GetUserRoutesResponse.from(routeResponse.map { UserRouteResponse.from(it) })
}

@Operation(
summary = "구매한 루트 목록 조회",
description = "로그인된 사용자의 구매한 루트 목록 조회 (미구현)",
security = [SecurityRequirement(name = "access-token")],
)
@GetMapping("/v1/users/me/purchased-routes")
fun getPurchasedRoutes(): GetPurchasedRouteResponse {
// TODO: 로직 구현

val dummy = listOf(
PurchasedRouteResponse(
routeId = 1,
routeName = "민속촌 산책로",
routeDescription = "민속촌 산책로",
routeImageUrl = "https://routebox-resources.s3.ap-northeast-2.amazonaws.com/image/1.jpg",
createdAt = "2024-08-31T00:00:00",
),
PurchasedRouteResponse(
routeId = 2,
routeName = "서울 랜드마크 투어",
routeDescription = null,
routeImageUrl = null,
createdAt = "2024-08-24T00:00:00",
),
PurchasedRouteResponse(
routeId = 3,
routeName = "부산 맛집 탐방",
routeDescription = "부산 맛집 탐방",
routeImageUrl = "https://routebox-resources.s3.ap-northeast-2.amazonaws.com/image/2.jpg",
createdAt = "2024-08-11T00:00:00",
),
PurchasedRouteResponse(
routeId = 4,
routeName = "경복궁 루트",
routeDescription = "경복궁 루트",
routeImageUrl = "https://routebox-resources.s3.ap-northeast-2.amazonaws.com/image/3.jpg",
createdAt = "2024-08-01T00:00:00",
),
)

val routes = GetPurchasedRouteResponse(
routes = dummy,
)
return routes
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.routebox.routebox.domain.purchased_route

import com.routebox.routebox.domain.common.BaseEntity
import com.routebox.routebox.domain.converter.StringArrayConverter
import com.routebox.routebox.domain.purchased_route.converter.PurchasedRouteActivityConverter
import com.routebox.routebox.domain.purchased_route.converter.PurchasedRoutePointConverter
import com.routebox.routebox.domain.purchased_route.converter.PurchasedRouteStylesConverter
import com.routebox.routebox.domain.route.Route
import com.routebox.routebox.domain.user.User
import jakarta.persistence.Column
Expand Down Expand Up @@ -30,12 +32,13 @@ class PurchasedRoute(
whoWith: String?,
numberOfPeoples: Int?,
numberOfDays: String?,
styles: Array<String>,
styles: List<String>,
transportation: String?,
routePoints: List<PurchasedRoutePoint>,
routeActivities: List<PurchasedRouteActivity>,
) : BaseEntity() {

companion object {
fun createFrom(route: Route, buyer: User): PurchasedRoute = PurchasedRoute(
fun fromRoute(route: Route, buyer: User): PurchasedRoute = PurchasedRoute(
buyer = buyer,
writer = route.user,
routeId = route.id,
Expand All @@ -46,8 +49,10 @@ class PurchasedRoute(
whoWith = route.whoWith,
numberOfPeoples = route.numberOfPeople,
numberOfDays = route.numberOfDays,
styles = route.style,
styles = route.style.toList(),
transportation = route.name,
routePoints = route.routePoints.map { PurchasedRoutePoint.fromRoutePoint(it) },
routeActivities = route.routeActivities.map { PurchasedRouteActivity.fromRouteActivity(it) },
)
}

Expand Down Expand Up @@ -87,14 +92,20 @@ class PurchasedRoute(
var numberOfDays: String? = numberOfDays
private set

@Convert(converter = StringArrayConverter::class)
@Column(columnDefinition = "json")
var styles: Array<String> = styles
@Convert(converter = PurchasedRouteStylesConverter::class)
var styles: List<String> = styles
private set

var transportation: String? = transportation
private set

// TODO: routePoints 추가
// TODO: routeActivities 추가
@Convert(converter = PurchasedRoutePointConverter::class)
@Column(columnDefinition = "JSON")
var routePoints: List<PurchasedRoutePoint> = routePoints
private set

@Convert(converter = PurchasedRouteActivityConverter::class)
@Column(columnDefinition = "JSON")
var routeActivities: List<PurchasedRouteActivity> = routeActivities
private set
}
Loading

0 comments on commit eb8283d

Please sign in to comment.