Skip to content

Commit

Permalink
Merge pull request #114 from wafflestudio/develop
Browse files Browse the repository at this point in the history
Release
  • Loading branch information
asp345 authored Sep 24, 2024
2 parents 892ed15 + 7b2a40b commit 3155ddf
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 1 deletion.
1 change: 0 additions & 1 deletion batch/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ dependencies {
implementation(project(":core"))

implementation("org.springframework.boot:spring-boot-starter-batch")
implementation("org.springframework.boot:spring-boot-starter-data-mongodb")
runtimeOnly("org.postgresql:postgresql")
runtimeOnly("com.h2database:h2")
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class SnuttLectureSyncJobConfig(
private val semesterLectureRepository: SemesterLectureRepository,
private val lectureRepository: LectureRepository,
private val snuttLectureIdMapRepository: SnuttLectureIdMapRepository,
private val ratingSyncJob: Job,
) {
companion object {
private const val JOB_NAME = "SYNC_JOB"
Expand Down Expand Up @@ -78,6 +79,7 @@ class SnuttLectureSyncJobConfig(
),
),
)
.next(ratingSyncJobStep(jobRepository))
.build()
}

Expand All @@ -93,6 +95,7 @@ class SnuttLectureSyncJobConfig(
.toMutableMap()
return JobBuilder(JOB_NAME, jobRepository)
.start(customReaderStep(jobRepository, Query()))
.next(ratingSyncJobStep(jobRepository))
.build()
}

Expand Down Expand Up @@ -169,6 +172,11 @@ class SnuttLectureSyncJobConfig(
snuttLectureIdMapRepository.saveAll(items.map { it.snuttLectureIdMap })
}
}

private fun ratingSyncJobStep(jobRepository: JobRepository): Step =
StepBuilder(SnuttRatingSyncJobConfig.RATING_SYNC_JOB_NAME, jobRepository)
.job(ratingSyncJob)
.build()
}

data class SyncProcessResult(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.wafflestudio.snuttev.sync

import com.wafflestudio.snuttev.core.domain.lecture.model.SnuttLectureIdMap
import com.wafflestudio.snuttev.core.domain.lecture.repository.LectureRepository
import jakarta.persistence.EntityManagerFactory
import org.springframework.batch.core.Job
import org.springframework.batch.core.Step
import org.springframework.batch.core.job.builder.JobBuilder
import org.springframework.batch.core.repository.JobRepository
import org.springframework.batch.core.step.builder.StepBuilder
import org.springframework.batch.item.ItemWriter
import org.springframework.batch.item.database.JpaPagingItemReader
import org.springframework.batch.item.database.builder.JpaPagingItemReaderBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
import org.springframework.data.mongodb.core.BulkOperations
import org.springframework.data.mongodb.core.MongoTemplate
import org.springframework.data.mongodb.core.query.Criteria
import org.springframework.data.mongodb.core.query.Query
import org.springframework.data.mongodb.core.query.Update
import org.springframework.orm.jpa.JpaTransactionManager

@Configuration
@Profile(value = ["!test"])
class SnuttRatingSyncJobConfig(
private val entityManagerFactory: EntityManagerFactory,
private val mongoTemplate: MongoTemplate,
private val lectureRepository: LectureRepository,
) {
companion object {
const val RATING_SYNC_JOB_NAME = "RATING_SYNC_JOB"
private const val CUSTOM_READER_JOB_STEP = RATING_SYNC_JOB_NAME + "_STEP"
private const val CHUNK_SIZE = 1000000
}

@Bean
fun ratingSyncJob(jobRepository: JobRepository): Job {
return JobBuilder(RATING_SYNC_JOB_NAME, jobRepository)
.start(customReaderStep(jobRepository))
.build()
}

private fun customReaderStep(jobRepository: JobRepository): Step {
return StepBuilder(CUSTOM_READER_JOB_STEP, jobRepository)
.chunk<SnuttLectureIdMap, SnuttLectureIdMap>(
CHUNK_SIZE,
JpaTransactionManager().apply {
this.entityManagerFactory = this@SnuttRatingSyncJobConfig.entityManagerFactory
},
)
.reader(reader())
.writer(writer())
.build()
}

private fun reader(): JpaPagingItemReader<SnuttLectureIdMap> =
JpaPagingItemReaderBuilder<SnuttLectureIdMap>()
.name("snuttLectureIdMapReader")
.entityManagerFactory(entityManagerFactory)
.queryString("SELECT s FROM SnuttLectureIdMap s JOIN FETCH s.semesterLecture")
.pageSize(CHUNK_SIZE)
.build()

private fun writer(): ItemWriter<SnuttLectureIdMap> {
return ItemWriter { items ->
val lectureIdtoLectureRatingMap =
lectureRepository.findAllRatingsByLectureIds(
items.mapNotNull { it.semesterLecture.lecture.id },
)
.associateBy { it.id }
val bulkOps = mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, "lectures")
items.forEach {
val evInfo = lectureIdtoLectureRatingMap[it.semesterLecture.lecture.id]
bulkOps.updateOne(
Query(Criteria.where("_id").`is`(it.snuttId)),
Update().set("evInfo.evId", evInfo?.id)
.set("evInfo.avgRating", evInfo?.avgRating)
.set("evInfo.count", evInfo?.count),
)
}
bulkOps.execute()
}
}
}
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ subprojects {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")

implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-data-mongodb")

implementation("com.wafflestudio.truffle.sdk:truffle-spring-boot-starter:1.1.2")
implementation("com.wafflestudio.truffle.sdk:truffle-logback:1.1.2")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ import com.wafflestudio.snuttev.core.domain.evaluation.repository.LectureEvaluat
import com.wafflestudio.snuttev.core.domain.lecture.model.SemesterLecture
import com.wafflestudio.snuttev.core.domain.lecture.repository.LectureRepository
import com.wafflestudio.snuttev.core.domain.lecture.repository.SemesterLectureRepository
import com.wafflestudio.snuttev.core.domain.lecture.repository.SnuttLectureIdMapRepository
import com.wafflestudio.snuttev.core.domain.mongo.MongoService
import com.wafflestudio.snuttev.core.domain.tag.repository.TagRepository
import org.springframework.dao.DataIntegrityViolationException
import org.springframework.data.repository.findByIdOrNull
Expand All @@ -51,6 +53,8 @@ class EvaluationService internal constructor(
private val evaluationReportRepository: EvaluationReportRepository,
private val evaluationLikeRepository: EvaluationLikeRepository,
private val cache: Cache,
private val snuttLectureIdMapRepository: SnuttLectureIdMapRepository,
private val mongoService: MongoService,
) {
companion object {
private const val DEFAULT_PAGE_SIZE = 20
Expand All @@ -77,6 +81,8 @@ class EvaluationService internal constructor(

cache.deleteAll(CacheKey.EVALUATIONS_BY_TAG_PAGE)

updateEvInfosBySemesterLecture(semesterLecture)

return genLectureEvaluationDto(lectureEvaluation)
}

Expand Down Expand Up @@ -276,6 +282,7 @@ class EvaluationService internal constructor(
}

cache.deleteAll(CacheKey.EVALUATIONS_BY_TAG_PAGE)
updateEvInfosBySemesterLecture(evaluation.semesterLecture)

val isLiked = evaluationLikeRepository.existsByLectureEvaluationAndUserId(evaluation, userId)
return EvaluationWithSemesterResponse.of(evaluation, userId, isLiked)
Expand Down Expand Up @@ -308,6 +315,7 @@ class EvaluationService internal constructor(
lectureEvaluation.isHidden = true

cache.deleteAll(CacheKey.EVALUATIONS_BY_TAG_PAGE)
updateEvInfosBySemesterLecture(lectureEvaluation.semesterLecture)
}

fun reportEvaluation(
Expand Down Expand Up @@ -391,4 +399,10 @@ class EvaluationService internal constructor(
content = evaluationReport.content,
isHidden = evaluationReport.isHidden,
)

private fun updateEvInfosBySemesterLecture(semesterLecture: SemesterLecture) {
val evInfo = lectureRepository.findAllRatingsByLectureIds(listOf(semesterLecture.lecture.id!!)).firstOrNull()
val snuttIds = snuttLectureIdMapRepository.findAllBySemesterLecture(semesterLecture).map { it.snuttId }
mongoService.updateEvInfoToSnuttIds(snuttIds, evInfo)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,13 @@ interface LectureRepository : JpaRepository<Lecture, Long?>, LectureRepositoryCu
""",
)
fun findAllRatingsByLectureIds(ids: Iterable<Long>): List<LectureRatingDao>

@Query(
"""
select new com.wafflestudio.snuttev.core.domain.lecture.model.LectureRatingDao(
sl.lecture.id, avg(le.rating), count(le.id)
) from LectureEvaluation le right join le.semesterLecture sl where le.isHidden = false group by sl.lecture.id
""",
)
fun findAllRatings(): List<LectureRatingDao>
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.wafflestudio.snuttev.core.domain.lecture.repository

import com.wafflestudio.snuttev.core.domain.lecture.model.SemesterLecture
import com.wafflestudio.snuttev.core.domain.lecture.model.SnuttLectureIdMap
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
Expand All @@ -8,4 +9,5 @@ interface SnuttLectureIdMapRepository : JpaRepository<SnuttLectureIdMap, Long> {
@Query("SELECT ttm FROM SnuttLectureIdMap ttm JOIN FETCH ttm.semesterLecture WHERE ttm.snuttId IN :snuttIds")
fun findAllWithSemesterLectureBySnuttIdIn(snuttIds: List<String>): List<SnuttLectureIdMap>
fun findBySnuttId(snuttId: String): SnuttLectureIdMap?
fun findAllBySemesterLecture(semesterLecture: SemesterLecture): List<SnuttLectureIdMap>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.wafflestudio.snuttev.core.domain.mongo

import com.wafflestudio.snuttev.core.domain.lecture.model.LectureRatingDao
import org.springframework.data.mongodb.core.MongoTemplate
import org.springframework.data.mongodb.core.query.Criteria
import org.springframework.data.mongodb.core.query.Query
import org.springframework.data.mongodb.core.query.Update
import org.springframework.stereotype.Service

@Service
class MongoService(
private val mongoTemplate: MongoTemplate,
) {
fun updateEvInfoToSnuttIds(snuttIds: List<String>, evInfo: LectureRatingDao?) =
runCatching {
mongoTemplate.updateMulti(
Query(Criteria.where("_id").`in`(snuttIds)),
Update().set("evInfo.evId", evInfo?.id)
.set("evInfo.avgRating", evInfo?.avgRating)
.set("evInfo.count", evInfo?.count),
"lectures",
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ import com.wafflestudio.snuttev.core.domain.lecture.model.Lecture
import com.wafflestudio.snuttev.core.domain.lecture.model.SemesterLecture
import com.wafflestudio.snuttev.core.domain.lecture.repository.LectureRepository
import com.wafflestudio.snuttev.core.domain.lecture.repository.SemesterLectureRepository
import com.wafflestudio.snuttev.core.domain.mongo.MongoService
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatNoException
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.data.repository.findByIdOrNull
import org.springframework.transaction.annotation.Transactional
import kotlin.random.Random
Expand All @@ -34,6 +36,7 @@ class EvaluationServiceTest @Autowired constructor(
private val lectureRepository: LectureRepository,
private val semesterLectureRepository: SemesterLectureRepository,
private val evaluationLikeRepository: EvaluationLikeRepository,
@MockBean private val mongoService: MongoService,
) {
@BeforeEach
fun setup() {
Expand Down

0 comments on commit 3155ddf

Please sign in to comment.