Skip to content

Commit

Permalink
Merge pull request #168 from mash-up-kr/feature/#67-cache-place-distance
Browse files Browse the repository at this point in the history
feat: 장소 거리 데이터 cache 적용
  • Loading branch information
thguss committed Aug 29, 2024
2 parents f92c318 + d43d17f commit c6407f3
Show file tree
Hide file tree
Showing 14 changed files with 166 additions and 118 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,17 @@ import com.piikii.application.domain.schedule.ScheduleType
data class CoursePlace(
val scheduleId: LongTypeId,
val scheduleType: ScheduleType,
val placeId: LongTypeId,
val name: String,
val url: String?,
val address: String?,
val phoneNumber: String?,
val coordinate: Coordinate?,
val distance: Distance?,
val prePlace: Place?,
val place: Place,
val coordinate: Coordinate,
val distance: Distance,
) {
companion object {
fun from(
schedule: Schedule,
place: Place,
coordinate: Coordinate?,
distance: Distance?,
): CoursePlace {
return CoursePlace(
scheduleId = schedule.id,
scheduleType = schedule.type,
placeId = place.id,
name = place.name,
url = place.url,
address = place.address,
phoneNumber = place.phoneNumber,
coordinate = coordinate,
distance = distance,
)
}
}
constructor(schedule: Schedule, prePlace: Place?, place: Place, distance: Distance) : this(
scheduleId = schedule.id,
scheduleType = schedule.type,
prePlace = prePlace,
place = place,
coordinate = place.getCoordinate(),
distance = distance,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,24 +82,15 @@ class CourseService(
places: List<Place>,
agreeCountByPlaceId: Map<Long, Int>,
): Map<Schedule, CoursePlace> {
// initial 값 설정: null과 빈 Map의 쌍으로 초기화
val initial: Map<Schedule, CoursePlace> = emptyMap()

val initCourse: Map<Schedule, CoursePlace> = emptyMap()
return mapPlacesBySchedule(schedules, places)
.entries.fold(initial) { prePlaceBySchedule, (schedule, places) ->
// 현재 CoursePlace 생성
.entries.fold(initCourse) { course, (schedule, places) ->
val confirmedPlace = getConfirmedPlace(schedule, places, agreeCountByPlaceId)

if (confirmedPlace != null) {
val preCoursePlace = prePlaceBySchedule.values.lastOrNull()
val curCoursePlace = getCoursePlace(schedule, preCoursePlace, confirmedPlace)

// currentCoursePlace를 누적된 placeBySchedule 맵에 추가
prePlaceBySchedule + (schedule to curCoursePlace)
} else {
// confirmedPlace가 null인 경우 기존 맵을 그대로 반환
prePlaceBySchedule
}
confirmedPlace?.let {
val preCoursePlace = course.values.lastOrNull()
val curCoursePlace = getCoursePlace(schedule, preCoursePlace?.place, it)
course + (schedule to curCoursePlace)
} ?: course
}
}

Expand All @@ -119,21 +110,17 @@ class CourseService(

private fun getCoursePlace(
schedule: Schedule,
preCoursePlace: CoursePlace?,
prePlace: Place?,
confirmedPlace: Place,
): CoursePlace {
val coordinate = confirmedPlace.getCoordinate()

return CoursePlace.from(
return CoursePlace(
schedule = schedule,
prePlace = prePlace,
place = confirmedPlace,
coordinate = coordinate,
distance =
preCoursePlace?.coordinate?.let { preCoordinate ->
coordinate.let { coordinate ->
navigationPort.getDistance(start = preCoordinate, end = coordinate)
}
},
prePlace?.let {
navigationPort.getDistance(it, confirmedPlace)
} ?: Distance.EMPTY,
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package com.piikii.application.domain.course

data class Distance(
val totalDistanceMeter: Int?,
val totalTimeMinute: Int?,
val totalDistanceMeter: Int? = null,
val totalTimeMinute: Int? = null,
) {
companion object {
@JvmField
val EMPTY = Distance(null, null)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,13 @@ data class CoursePlaceResponse(
return CoursePlaceResponse(
scheduleId = coursePlace.scheduleId.getValue(),
scheduleType = coursePlace.scheduleType,
placeId = coursePlace.placeId.getValue(),
name = coursePlace.name,
url = coursePlace.url,
address = coursePlace.address,
phoneNumber = coursePlace.phoneNumber,
distance = coursePlace.distance?.totalDistanceMeter,
time = coursePlace.distance?.totalTimeMinute,
placeId = coursePlace.place.id.getValue(),
name = coursePlace.place.name,
url = coursePlace.place.url,
address = coursePlace.place.address,
phoneNumber = coursePlace.place.phoneNumber,
distance = coursePlace.distance.totalDistanceMeter,
time = coursePlace.distance.totalTimeMinute,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.piikii.application.port.output.web

import com.piikii.application.domain.course.Coordinate
import com.piikii.application.domain.course.Distance
import com.piikii.application.domain.place.Place

interface NavigationPort {
fun getDistance(
start: Coordinate,
end: Coordinate,
startPlace: Place,
endPlace: Place,
): Distance
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,7 @@ class CourseServiceTest {
),
)

val coordinate1 = Coordinate(places[0].longitude, places[0].latitude)
val coordinate2 = Coordinate(places[2].longitude, places[2].latitude)
given(navigationPort.getDistance(coordinate1, coordinate2))
given(navigationPort.getDistance(places[0], places[2]))
.willReturn(Distance(100, 5))

val updatedPlace = places[2].copy(confirmed = true)
Expand Down
1 change: 1 addition & 0 deletions piikii-bootstrap/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ spring:
- classpath:avocado-config/application.yml
- classpath:tmap-config/application.yml
- classpath:application-actuator.yml
- classpath:cache-config/application.yml
application:
name: "piikii"
messages:
Expand Down
1 change: 1 addition & 0 deletions piikii-bootstrap/src/test/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ spring:
- classpath:avocado-config/application.yml
- classpath:database-config/application-test.yml
- classpath:tmap-config/application.yml
- classpath:cache-config/application.yml
application:
name: "piikii"
messages:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,48 +1,82 @@
package com.piikii.output.redis.config

import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.MapperFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.databind.json.JsonMapper
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.cache.CacheManager
import org.springframework.cache.annotation.EnableCaching
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.data.redis.cache.RedisCacheConfiguration
import org.springframework.data.redis.cache.RedisCacheManager
import org.springframework.data.redis.connection.RedisConnectionFactory
import org.springframework.data.redis.connection.RedisPassword
import org.springframework.data.redis.connection.RedisStandaloneConfiguration
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer
import org.springframework.data.redis.serializer.RedisSerializationContext
import org.springframework.data.redis.serializer.StringRedisSerializer
import java.time.Duration

// TODO: 필요 시에 등록을 위해 설정
// @Configuration
@Configuration
@EnableRedisRepositories
@EnableCaching
@EnableConfigurationProperties(RedisProperties::class)
class RedisConfig {
@Value("\${redis.host}")
private val redisHost: String? = null
@Bean
fun lettuceConnectionFactory(redisProperties: RedisProperties): LettuceConnectionFactory {
val redisConfig =
RedisStandaloneConfiguration().apply {
hostName = redisProperties.host
port = redisProperties.port
password = RedisPassword.of(redisProperties.password)
}

@Value("\${redis.port}")
private val redisPort = 0
val clientConfig =
LettuceClientConfiguration.builder()
.useSsl()
.build()

@Bean
fun objectMapper(): ObjectMapper {
return JsonMapper.builder()
.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS, true)
.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false)
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true)
.findAndAddModules()
.build()
return LettuceConnectionFactory(redisConfig, clientConfig)
}

@Bean
fun lettuceConnectionFactory(): LettuceConnectionFactory {
return LettuceConnectionFactory(redisHost!!, redisPort)
fun <T> redisTemplate(
redisConnectionFactory: RedisConnectionFactory,
objectMapper: ObjectMapper,
): RedisTemplate<String, T> {
val redisTemplate = RedisTemplate<String, T>()
redisTemplate.connectionFactory = redisConnectionFactory
redisTemplate.keySerializer = StringRedisSerializer()
redisTemplate.valueSerializer = GenericJackson2JsonRedisSerializer(objectMapper)
return redisTemplate
}

@Bean
fun redistemplate(): RedisTemplate<String, Any> {
val redisTemplate = RedisTemplate<String, Any>()
redisTemplate.connectionFactory = lettuceConnectionFactory()
redisTemplate.keySerializer = StringRedisSerializer()
redisTemplate.valueSerializer = GenericJackson2JsonRedisSerializer(objectMapper())
return redisTemplate
fun cacheManager(redisConnectionFactory: RedisConnectionFactory): CacheManager {
val redisCacheConfiguration =
RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofDays(7))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer()))
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(
GenericJackson2JsonRedisSerializer(),
),
)

return RedisCacheManager.RedisCacheManagerBuilder
.fromConnectionFactory(redisConnectionFactory)
.cacheDefaults(redisCacheConfiguration)
.build()
}
}

@ConfigurationProperties(prefix = "redis")
data class RedisProperties(
val host: String,
val port: Int,
val password: String,
)

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
redis:
host: ${REDIS_HOST}
port: 6379
password: ${REDIS_PASSWORD}
ssl: true
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package com.piikii.output.web.tmap.adapter

import com.piikii.application.domain.course.Coordinate
import com.piikii.application.domain.course.Distance
import com.piikii.application.domain.place.Place
import com.piikii.application.port.output.web.NavigationPort
import com.piikii.common.exception.ExceptionCode
import com.piikii.common.exception.PiikiiException
import org.springframework.cache.annotation.Cacheable
import org.springframework.stereotype.Component
import org.springframework.web.client.RestClient
import org.springframework.web.client.body
Expand All @@ -13,12 +15,19 @@ import org.springframework.web.client.body
class TmapNavigationAdapter(
private val tmapApiClient: RestClient,
) : NavigationPort {
@Cacheable(
value = ["Distance"],
key = "#startPlace.id + '_' + #endPlace.id",
unless = "#result == T(com.piikii.application.domain.course.Distance).EMPTY",
)
override fun getDistance(
start: Coordinate,
end: Coordinate,
startPlace: Place,
endPlace: Place,
): Distance {
return if (start.isValid() && end.isValid()) {
getDistanceFromTmap(start, end)
val startCoordinate = startPlace.getCoordinate()
val endCoordinate = endPlace.getCoordinate()
return if (startCoordinate.isValid() && endCoordinate.isValid()) {
getDistanceFromTmap(startCoordinate, endCoordinate)
} else {
Distance.EMPTY
}
Expand Down
Loading

0 comments on commit c6407f3

Please sign in to comment.