diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/AdminRecommendSeriesController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/AdminRecommendSeriesController.kt new file mode 100644 index 0000000..bc747b1 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/AdminRecommendSeriesController.kt @@ -0,0 +1,40 @@ +package kr.co.vividnext.sodalive.admin.content.series.recommend + +import kr.co.vividnext.sodalive.common.ApiResponse +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.PutMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RequestPart +import org.springframework.web.bind.annotation.RestController +import org.springframework.web.multipart.MultipartFile + +@RestController +@PreAuthorize("hasRole('ADMIN')") +@RequestMapping("/admin/audio-content/series/recommend") +class AdminRecommendSeriesController(private val service: AdminRecommendSeriesService) { + @GetMapping + fun getRecommendSeriesList(@RequestParam isFree: Boolean) = ApiResponse.ok( + service.getRecommendSeriesList(isFree = isFree) + ) + + @PostMapping + fun createRecommendSeries( + @RequestPart("image") image: MultipartFile, + @RequestPart("request") requestString: String + ) = ApiResponse.ok(service.createRecommendSeries(image, requestString)) + + @PutMapping + fun modifyRecommendSeries( + @RequestPart("image", required = false) image: MultipartFile? = null, + @RequestPart("request") requestString: String + ) = ApiResponse.ok(service.updateRecommendSeries(image, requestString)) + + @PutMapping("/orders") + fun updateRecommendSeriesOrders( + @RequestBody request: UpdateRecommendSeriesOrdersRequest + ) = ApiResponse.ok(service.updateRecommendSeriesOrders(request.ids)) +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/AdminRecommendSeriesRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/AdminRecommendSeriesRepository.kt new file mode 100644 index 0000000..04b47f8 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/AdminRecommendSeriesRepository.kt @@ -0,0 +1,43 @@ +package kr.co.vividnext.sodalive.admin.content.series.recommend + +import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.content.main.tab.QRecommendSeries.recommendSeries +import kr.co.vividnext.sodalive.content.main.tab.RecommendSeries +import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series +import org.springframework.beans.factory.annotation.Value +import org.springframework.data.jpa.repository.JpaRepository + +interface AdminRecommendSeriesRepository : + JpaRepository, + AdminRecommendSeriesQueryRepository + +interface AdminRecommendSeriesQueryRepository { + fun getRecommendSeriesList(isFree: Boolean): List +} + +class AdminRecommendSeriesQueryRepositoryImpl( + private val queryFactory: JPAQueryFactory, + + @Value("\${cloud.aws.cloud-front.host}") + private val imageHost: String +) : AdminRecommendSeriesQueryRepository { + override fun getRecommendSeriesList(isFree: Boolean): List { + return queryFactory + .select( + QGetAdminRecommendSeriesListResponse( + recommendSeries.id, + series.id, + series.title, + recommendSeries.imagePath.prepend("/").prepend(imageHost) + ) + ) + .from(recommendSeries) + .innerJoin(recommendSeries.series, series) + .where( + recommendSeries.isActive.isTrue + .and(series.isActive.isTrue) + .and(recommendSeries.isFree.eq(isFree)) + ) + .fetch() + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/AdminRecommendSeriesService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/AdminRecommendSeriesService.kt new file mode 100644 index 0000000..cf697e4 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/AdminRecommendSeriesService.kt @@ -0,0 +1,87 @@ +package kr.co.vividnext.sodalive.admin.content.series.recommend + +import com.fasterxml.jackson.databind.ObjectMapper +import kr.co.vividnext.sodalive.admin.content.series.AdminContentSeriesRepository +import kr.co.vividnext.sodalive.aws.s3.S3Uploader +import kr.co.vividnext.sodalive.common.SodaException +import kr.co.vividnext.sodalive.content.main.tab.RecommendSeries +import kr.co.vividnext.sodalive.utils.generateFileName +import org.springframework.beans.factory.annotation.Value +import org.springframework.data.repository.findByIdOrNull +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import org.springframework.web.multipart.MultipartFile + +@Service +class AdminRecommendSeriesService( + private val s3Uploader: S3Uploader, + private val repository: AdminRecommendSeriesRepository, + private val seriesRepository: AdminContentSeriesRepository, + private val objectMapper: ObjectMapper, + + @Value("\${cloud.aws.s3.bucket}") + private val bucket: String +) { + fun getRecommendSeriesList(isFree: Boolean): List { + return repository.getRecommendSeriesList(isFree = isFree) + } + + @Transactional + fun createRecommendSeries(image: MultipartFile, requestString: String) { + val request = objectMapper.readValue(requestString, CreateRecommendSeriesRequest::class.java) + val series = seriesRepository.findByIdOrNull(request.seriesId) + ?: throw SodaException("잘못된 요청입니다.") + + val recommendSeries = RecommendSeries(isFree = request.isFree) + recommendSeries.series = series + repository.save(recommendSeries) + + val fileName = generateFileName() + val imagePath = s3Uploader.upload( + inputStream = image.inputStream, + bucket = bucket, + filePath = "recommend_series/${recommendSeries.id}/$fileName" + ) + recommendSeries.imagePath = imagePath + } + + @Transactional + fun updateRecommendSeries(image: MultipartFile?, requestString: String) { + val request = objectMapper.readValue(requestString, UpdateRecommendSeriesRequest::class.java) + val recommendSeries = repository.findByIdOrNull(request.id) + ?: throw SodaException("잘못된 요청입니다.") + + if (image != null) { + val fileName = generateFileName() + val imagePath = s3Uploader.upload( + inputStream = image.inputStream, + bucket = bucket, + filePath = "recommend_series/${recommendSeries.id}/$fileName" + ) + recommendSeries.imagePath = imagePath + } + + if (request.isActive != null) { + recommendSeries.isActive = request.isActive + } + + if (request.seriesId != null) { + val series = seriesRepository.findByIdOrNull(request.seriesId) + + if (series != null) { + recommendSeries.series = series + } + } + } + + @Transactional + fun updateRecommendSeriesOrders(ids: List) { + for (index in ids.indices) { + val recommendSeries = repository.findByIdOrNull(ids[index]) + + if (recommendSeries != null) { + recommendSeries.orders = index + 1 + } + } + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/CreateRecommendSeriesRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/CreateRecommendSeriesRequest.kt new file mode 100644 index 0000000..7bb67be --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/CreateRecommendSeriesRequest.kt @@ -0,0 +1,6 @@ +package kr.co.vividnext.sodalive.admin.content.series.recommend + +data class CreateRecommendSeriesRequest( + val seriesId: Long, + val isFree: Boolean +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/GetAdminRecommendSeriesListResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/GetAdminRecommendSeriesListResponse.kt new file mode 100644 index 0000000..ec9b039 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/GetAdminRecommendSeriesListResponse.kt @@ -0,0 +1,10 @@ +package kr.co.vividnext.sodalive.admin.content.series.recommend + +import com.querydsl.core.annotations.QueryProjection + +data class GetAdminRecommendSeriesListResponse @QueryProjection constructor( + val id: Long, + val seriesId: Long, + val seriesTitle: String, + val imageUrl: String +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/UpdateRecommendSeriesOrdersRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/UpdateRecommendSeriesOrdersRequest.kt new file mode 100644 index 0000000..90038e0 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/UpdateRecommendSeriesOrdersRequest.kt @@ -0,0 +1,5 @@ +package kr.co.vividnext.sodalive.admin.content.series.recommend + +data class UpdateRecommendSeriesOrdersRequest( + val ids: List +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/UpdateRecommendSeriesRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/UpdateRecommendSeriesRequest.kt new file mode 100644 index 0000000..d820c74 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/UpdateRecommendSeriesRequest.kt @@ -0,0 +1,7 @@ +package kr.co.vividnext.sodalive.admin.content.series.recommend + +data class UpdateRecommendSeriesRequest( + val id: Long, + val seriesId: Long?, + val isActive: Boolean? +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/RecommendSeries.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/RecommendSeries.kt new file mode 100644 index 0000000..9d0290d --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/RecommendSeries.kt @@ -0,0 +1,28 @@ +package kr.co.vividnext.sodalive.content.main.tab + +import kr.co.vividnext.sodalive.common.BaseEntity +import kr.co.vividnext.sodalive.creator.admin.content.series.Series +import javax.persistence.Column +import javax.persistence.Entity +import javax.persistence.FetchType +import javax.persistence.JoinColumn +import javax.persistence.ManyToOne + +@Entity +data class RecommendSeries( + @Column(nullable = false) + var imagePath: String = "", + + @Column(nullable = false) + var orders: Int = 1, + + @Column(nullable = false) + var isFree: Boolean = false, + + @Column(nullable = false) + var isActive: Boolean = true +) : BaseEntity() { + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "series_id", nullable = false) + var series: Series? = null +}