From 4089590bdf5743cd5772c3e4874f7bc50c645446 Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 16 Apr 2024 10:50:20 +0900 Subject: [PATCH 01/11] =?UTF-8?q?=EC=95=88=EB=93=9C=EB=A1=9C=EC=9D=B4?= =?UTF-8?q?=EB=93=9C=20=EC=9D=B8=20=EC=95=B1=20=EA=B2=B0=EC=A0=9C=20-=20co?= =?UTF-8?q?nsume=20=EC=9E=AC=EC=8B=9C=EB=8F=84=205=EB=B2=88=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/kr/co/vividnext/sodalive/can/charge/ChargeService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeService.kt index 31e9310..c95e582 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeService.kt @@ -258,7 +258,7 @@ class ChargeService( private fun consumeWithRetry(productId: String, purchaseToken: String, charge: Charge, member: Member): Boolean { var attempt = 0 var delay = 2000L - val retries = 3 + val retries = 5 var lastError: Exception? = null From 501fa36fada1f443cd0ece4e318af8d168683be9 Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 16 Apr 2024 19:42:29 +0900 Subject: [PATCH 02/11] =?UTF-8?q?=ED=81=AC=EB=A6=AC=EC=97=90=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EA=B4=80=EB=A6=AC=EC=9E=90=20API=20-=20=EC=8B=9C?= =?UTF-8?q?=EB=A6=AC=EC=A6=88=20=EC=83=9D=EC=84=B1=20API=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../content/series/CreateSeriesRequest.kt | 38 +++++++++ .../CreatorAdminContentSeriesController.kt | 28 +++++++ .../CreatorAdminContentSeriesRepository.kt | 12 +++ .../CreatorAdminContentSeriesService.kt | 79 +++++++++++++++++++ .../creator/admin/content/series/Series.kt | 11 +++ .../admin/content/series/SeriesContent.kt | 34 ++++++++ ...reatorAdminContentSeriesGenreRepository.kt | 11 +++ .../content/series/keyword/SeriesKeyword.kt | 19 ++++- 8 files changed, 229 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreateSeriesRequest.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesRepository.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/SeriesContent.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreateSeriesRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreateSeriesRequest.kt new file mode 100644 index 0000000..658c1cc --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreateSeriesRequest.kt @@ -0,0 +1,38 @@ +package kr.co.vividnext.sodalive.creator.admin.content.series + +import kr.co.vividnext.sodalive.common.SodaException + +data class CreateSeriesRequest( + val title: String, + val introduction: String, + val publishedDaysOfWeek: Set, + val keyword: String, + val genreId: Long = 0, + val isAdult: Boolean = false, + val writer: String? = null, + val studio: String? = null +) { + fun toSeries(): Series { + validate() + + return Series( + title = title, + introduction = introduction, + writer = writer, + studio = studio, + publishedDaysOfWeek = publishedDaysOfWeek, + isAdult = isAdult + ) + } + + private fun validate() { + if (title.isBlank()) throw SodaException("시리즈 제목을 입력하세요") + if (introduction.isBlank()) throw SodaException("시리즈 소개를 입력하세요") + if (keyword.isBlank()) throw SodaException("시리즈를 설명할 수 있는 키워드를 입력하세요") + if (genreId <= 0) throw SodaException("올바른 장르를 선택하세요") + if (publishedDaysOfWeek.isEmpty()) throw SodaException("시리즈 연재요일을 선택하세요") + if (publishedDaysOfWeek.contains(SeriesPublishedDaysOfWeek.RANDOM) && publishedDaysOfWeek.size > 1) { + throw SodaException("랜덤과 연재요일 동시에 선택할 수 없습니다.") + } + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt new file mode 100644 index 0000000..c288fdb --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt @@ -0,0 +1,28 @@ +package kr.co.vividnext.sodalive.creator.admin.content.series + +import kr.co.vividnext.sodalive.common.ApiResponse +import kr.co.vividnext.sodalive.common.SodaException +import kr.co.vividnext.sodalive.member.Member +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestPart +import org.springframework.web.bind.annotation.RestController +import org.springframework.web.multipart.MultipartFile + +@RestController +@PreAuthorize("hasRole('CREATOR')") +@RequestMapping("/creator-admin/audio-content/series") +class CreatorAdminContentSeriesController(private val service: CreatorAdminContentSeriesService) { + @PostMapping + fun createSeries( + @RequestPart("image") image: MultipartFile?, + @RequestPart("request") requestString: String, + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? + ) = run { + if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + + ApiResponse.ok(service.createSeries(image, requestString, member), "시리즈가 생성되었습니다.") + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesRepository.kt new file mode 100644 index 0000000..262ef25 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesRepository.kt @@ -0,0 +1,12 @@ +package kr.co.vividnext.sodalive.creator.admin.content.series + +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.data.jpa.repository.JpaRepository + +interface CreatorAdminContentSeriesRepository : JpaRepository, CreatorAdminContentSeriesQueryRepository + +interface CreatorAdminContentSeriesQueryRepository + +class CreatorAdminContentSeriesQueryRepositoryImpl( + private val queryFactory: JPAQueryFactory +) : CreatorAdminContentSeriesQueryRepository diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt new file mode 100644 index 0000000..d7f5bcd --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt @@ -0,0 +1,79 @@ +package kr.co.vividnext.sodalive.creator.admin.content.series + +import com.amazonaws.services.s3.model.ObjectMetadata +import com.fasterxml.jackson.databind.ObjectMapper +import kr.co.vividnext.sodalive.aws.s3.S3Uploader +import kr.co.vividnext.sodalive.common.SodaException +import kr.co.vividnext.sodalive.content.hashtag.HashTag +import kr.co.vividnext.sodalive.content.hashtag.HashTagRepository +import kr.co.vividnext.sodalive.creator.admin.content.series.genre.CreatorAdminContentSeriesGenreRepository +import kr.co.vividnext.sodalive.creator.admin.content.series.keyword.SeriesKeyword +import kr.co.vividnext.sodalive.member.Member +import kr.co.vividnext.sodalive.utils.generateFileName +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import org.springframework.web.multipart.MultipartFile + +@Service +class CreatorAdminContentSeriesService( + private val repository: CreatorAdminContentSeriesRepository, + private val genreRepository: CreatorAdminContentSeriesGenreRepository, + private val hashTagRepository: HashTagRepository, + + private val s3Uploader: S3Uploader, + private val objectMapper: ObjectMapper, + + @Value("\${cloud.aws.s3.bucket}") + private val coverImageBucket: String +) { + @Transactional + fun createSeries(coverImage: MultipartFile?, requestString: String, member: Member) { + if (coverImage == null) throw SodaException("커버이미지를 선택해 주세요.") + + val request = objectMapper.readValue(requestString, CreateSeriesRequest::class.java) + val series = request.toSeries() + val genre = genreRepository.findActiveSeriesGenreById(request.genreId) + series.genre = genre + repository.save(series) + + val keywords = request.keyword + .replace("#", " #") + .split(" ") + .map { it.trim() } + .filter { it.isNotBlank() } + .map { + val tag = if (!it.startsWith("#")) { + "#$it" + } else { + it + } + + val hashTag = hashTagRepository.findByTag(tag) + ?: hashTagRepository.save(HashTag(tag)) + + val seriesKeyword = SeriesKeyword() + seriesKeyword.series = series + seriesKeyword.keyword = hashTag + + seriesKeyword + } + + series.keywordList.addAll(keywords) + + val metadata = ObjectMetadata() + metadata.contentLength = coverImage.size + + // 커버 이미지 파일명 생성 + val coverImageFileName = generateFileName(prefix = "${series.id}-cover") + // 커버 이미지 업로드 + val coverImagePath = s3Uploader.upload( + inputStream = coverImage.inputStream, + bucket = coverImageBucket, + filePath = "series_cover/${series.id}/$coverImageFileName", + metadata = metadata + ) + + series.coverImage = coverImagePath + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/Series.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/Series.kt index b8c7521..3590f5a 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/Series.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/Series.kt @@ -2,6 +2,8 @@ package kr.co.vividnext.sodalive.creator.admin.content.series import kr.co.vividnext.sodalive.admin.content.series.genre.SeriesGenre import kr.co.vividnext.sodalive.common.BaseEntity +import kr.co.vividnext.sodalive.creator.admin.content.series.keyword.SeriesKeyword +import javax.persistence.CascadeType import javax.persistence.CollectionTable import javax.persistence.Column import javax.persistence.ElementCollection @@ -10,6 +12,7 @@ import javax.persistence.EnumType import javax.persistence.Enumerated import javax.persistence.FetchType import javax.persistence.JoinColumn +import javax.persistence.OneToMany import javax.persistence.OneToOne enum class SeriesPublishedDaysOfWeek { @@ -27,6 +30,8 @@ data class Series( var introduction: String, @Enumerated(value = EnumType.STRING) var state: SeriesState = SeriesState.PROCEEDING, + var writer: String? = null, + var studio: String? = null, @ElementCollection(targetClass = SeriesPublishedDaysOfWeek::class, fetch = FetchType.EAGER) @Enumerated(value = EnumType.STRING) @CollectionTable(name = "series_published_days_of_week", joinColumns = [JoinColumn(name = "series_id")]) @@ -39,4 +44,10 @@ data class Series( var genre: SeriesGenre? = null var coverImage: String? = null + + @OneToMany(mappedBy = "series", cascade = [CascadeType.ALL]) + var contentList: MutableList = mutableListOf() + + @OneToMany(mappedBy = "series", cascade = [CascadeType.ALL]) + var keywordList: MutableList = mutableListOf() } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/SeriesContent.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/SeriesContent.kt new file mode 100644 index 0000000..eabc927 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/SeriesContent.kt @@ -0,0 +1,34 @@ +package kr.co.vividnext.sodalive.creator.admin.content.series + +import kr.co.vividnext.sodalive.content.AudioContent +import java.time.LocalDateTime +import javax.persistence.Entity +import javax.persistence.FetchType +import javax.persistence.GeneratedValue +import javax.persistence.GenerationType +import javax.persistence.Id +import javax.persistence.JoinColumn +import javax.persistence.ManyToOne +import javax.persistence.PrePersist + +@Entity +class SeriesContent { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null + + var createdAt: LocalDateTime? = null + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "series_id", nullable = false) + var series: Series? = null + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "content_id", nullable = false) + var content: AudioContent? = null + + @PrePersist + fun prePersist() { + createdAt = LocalDateTime.now() + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/genre/CreatorAdminContentSeriesGenreRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/genre/CreatorAdminContentSeriesGenreRepository.kt index e35112d..78fcc34 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/genre/CreatorAdminContentSeriesGenreRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/genre/CreatorAdminContentSeriesGenreRepository.kt @@ -9,12 +9,23 @@ interface CreatorAdminContentSeriesGenreRepository : JpaRepository, CreatorAdminContentSeriesGenreQueryRepository interface CreatorAdminContentSeriesGenreQueryRepository { + fun findActiveSeriesGenreById(id: Long): SeriesGenre fun getGenreList(isAdult: Boolean): List } class CreatorAdminContentSeriesGenreQueryRepositoryImpl( private val queryFactory: JPAQueryFactory ) : CreatorAdminContentSeriesGenreQueryRepository { + override fun findActiveSeriesGenreById(id: Long): SeriesGenre { + return queryFactory + .selectFrom(seriesGenre) + .where( + seriesGenre.id.eq(id) + .and(seriesGenre.isActive.isTrue) + ) + .fetchFirst() + } + override fun getGenreList(isAdult: Boolean): List { var where = seriesGenre.isActive.isTrue diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/keyword/SeriesKeyword.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/keyword/SeriesKeyword.kt index 32d90dd..d36e591 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/keyword/SeriesKeyword.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/keyword/SeriesKeyword.kt @@ -1,15 +1,25 @@ package kr.co.vividnext.sodalive.creator.admin.content.series.keyword -import kr.co.vividnext.sodalive.common.BaseEntity import kr.co.vividnext.sodalive.content.hashtag.HashTag import kr.co.vividnext.sodalive.creator.admin.content.series.Series +import java.time.LocalDateTime import javax.persistence.Entity import javax.persistence.FetchType +import javax.persistence.GeneratedValue +import javax.persistence.GenerationType +import javax.persistence.Id import javax.persistence.JoinColumn import javax.persistence.ManyToOne +import javax.persistence.PrePersist @Entity -class SeriesKeyword : BaseEntity() { +class SeriesKeyword { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null + + var createdAt: LocalDateTime? = null + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "series_id", nullable = false) var series: Series? = null @@ -18,5 +28,8 @@ class SeriesKeyword : BaseEntity() { @JoinColumn(name = "keyword_id", nullable = false) var keyword: HashTag? = null - var isActive: Boolean = true + @PrePersist + fun prePersist() { + createdAt = LocalDateTime.now() + } } From 3b008155e1f78e7d54db8e533451ee852bf8e840 Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 16 Apr 2024 23:13:05 +0900 Subject: [PATCH 03/11] =?UTF-8?q?=ED=81=AC=EB=A6=AC=EC=97=90=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EA=B4=80=EB=A6=AC=EC=9E=90=20API=20-=20=EC=8B=9C?= =?UTF-8?q?=EB=A6=AC=EC=A6=88=20=EC=88=98=EC=A0=95/=EC=82=AD=EC=A0=9C=20AP?= =?UTF-8?q?I=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CreatorAdminContentSeriesController.kt | 15 ++++ .../CreatorAdminContentSeriesRepository.kt | 17 +++- .../CreatorAdminContentSeriesService.kt | 80 +++++++++++++++++++ .../content/series/ModifySeriesRequest.kt | 13 +++ .../creator/admin/content/series/Series.kt | 6 ++ 5 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/ModifySeriesRequest.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt index c288fdb..9f6fc90 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt @@ -3,9 +3,11 @@ package kr.co.vividnext.sodalive.creator.admin.content.series import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.member.Member +import org.springframework.lang.Nullable import org.springframework.security.access.prepost.PreAuthorize import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.PutMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestPart import org.springframework.web.bind.annotation.RestController @@ -25,4 +27,17 @@ class CreatorAdminContentSeriesController(private val service: CreatorAdminConte ApiResponse.ok(service.createSeries(image, requestString, member), "시리즈가 생성되었습니다.") } + + @PutMapping + fun modifySeries( + @Nullable + @RequestPart("image") + image: MultipartFile?, + @RequestPart("request") requestString: String, + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? + ) = run { + if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + + ApiResponse.ok(service.modifySeries(image, requestString, member), "시리즈가 수정되었습니다.") + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesRepository.kt index 262ef25..0c06767 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesRepository.kt @@ -1,12 +1,25 @@ package kr.co.vividnext.sodalive.creator.admin.content.series import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series import org.springframework.data.jpa.repository.JpaRepository interface CreatorAdminContentSeriesRepository : JpaRepository, CreatorAdminContentSeriesQueryRepository -interface CreatorAdminContentSeriesQueryRepository +interface CreatorAdminContentSeriesQueryRepository { + fun findByIdAndCreatorId(id: Long, creatorId: Long): Series? +} class CreatorAdminContentSeriesQueryRepositoryImpl( private val queryFactory: JPAQueryFactory -) : CreatorAdminContentSeriesQueryRepository +) : CreatorAdminContentSeriesQueryRepository { + override fun findByIdAndCreatorId(id: Long, creatorId: Long): Series? { + return queryFactory + .selectFrom(series) + .where( + series.id.eq(id) + .and(series.member.id.eq(creatorId)) + ) + .fetchFirst() + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt index d7f5bcd..3721d12 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt @@ -35,6 +35,7 @@ class CreatorAdminContentSeriesService( val series = request.toSeries() val genre = genreRepository.findActiveSeriesGenreById(request.genreId) series.genre = genre + series.member = member repository.save(series) val keywords = request.keyword @@ -76,4 +77,83 @@ class CreatorAdminContentSeriesService( series.coverImage = coverImagePath } + + @Transactional + fun modifySeries(coverImage: MultipartFile?, requestString: String, member: Member) { + val request = objectMapper.readValue(requestString, ModifySeriesRequest::class.java) + + if ( + coverImage == null && + request.title == null && + request.introduction == null && + request.publishedDaysOfWeek == null && + request.genreId == null && + request.isAdult == null && + request.writer == null && + request.studio == null + ) { + throw SodaException("변경사항이 없습니다.") + } + + val series = repository.findByIdAndCreatorId(id = request.seriesId, creatorId = member.id!!) + ?: throw SodaException("잘못된 접근입니다.") + + if (coverImage != null) { + val metadata = ObjectMetadata() + metadata.contentLength = coverImage.size + + // 커버 이미지 파일명 생성 + val coverImageFileName = generateFileName(prefix = "${series.id}-cover") + // 커버 이미지 업로드 + val coverImagePath = s3Uploader.upload( + inputStream = coverImage.inputStream, + bucket = coverImageBucket, + filePath = "series_cover/${series.id}/$coverImageFileName", + metadata = metadata + ) + + series.coverImage = coverImagePath + } + + if (request.title != null) { + series.title = request.title + } + + if (request.introduction != null) { + series.introduction = request.introduction + } + + if (request.publishedDaysOfWeek != null) { + if ( + request.publishedDaysOfWeek.contains(SeriesPublishedDaysOfWeek.RANDOM) && + request.publishedDaysOfWeek.size > 1 + ) { + throw SodaException("랜덤과 연재요일 동시에 선택할 수 없습니다.") + } + + series.publishedDaysOfWeek.toMutableSet().clear() + series.publishedDaysOfWeek.toMutableSet().addAll(request.publishedDaysOfWeek) + } + + if (request.genreId != null) { + val genre = genreRepository.findActiveSeriesGenreById(request.genreId) + series.genre = genre + } + + if (request.isAdult != null) { + series.isAdult = request.isAdult + } + + if (request.isActive != null) { + series.isActive = request.isActive + } + + if (request.writer != null) { + series.writer = request.writer + } + + if (request.studio != null) { + series.studio = request.studio + } + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/ModifySeriesRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/ModifySeriesRequest.kt new file mode 100644 index 0000000..4057c55 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/ModifySeriesRequest.kt @@ -0,0 +1,13 @@ +package kr.co.vividnext.sodalive.creator.admin.content.series + +data class ModifySeriesRequest( + val seriesId: Long, + val title: String?, + val introduction: String?, + val publishedDaysOfWeek: Set?, + val genreId: Long?, + val isAdult: Boolean?, + val isActive: Boolean?, + val writer: String?, + val studio: String? +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/Series.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/Series.kt index 3590f5a..504a180 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/Series.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/Series.kt @@ -3,6 +3,7 @@ package kr.co.vividnext.sodalive.creator.admin.content.series import kr.co.vividnext.sodalive.admin.content.series.genre.SeriesGenre import kr.co.vividnext.sodalive.common.BaseEntity import kr.co.vividnext.sodalive.creator.admin.content.series.keyword.SeriesKeyword +import kr.co.vividnext.sodalive.member.Member import javax.persistence.CascadeType import javax.persistence.CollectionTable import javax.persistence.Column @@ -12,6 +13,7 @@ import javax.persistence.EnumType import javax.persistence.Enumerated import javax.persistence.FetchType import javax.persistence.JoinColumn +import javax.persistence.ManyToOne import javax.persistence.OneToMany import javax.persistence.OneToOne @@ -43,6 +45,10 @@ data class Series( @JoinColumn(name = "genre_id", nullable = false) var genre: SeriesGenre? = null + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + var member: Member? = null + var coverImage: String? = null @OneToMany(mappedBy = "series", cascade = [CascadeType.ALL]) From 2a9eaead832219eca0b0d49671df091b315bb0e0 Mon Sep 17 00:00:00 2001 From: Klaus Date: Wed, 17 Apr 2024 00:02:26 +0900 Subject: [PATCH 04/11] =?UTF-8?q?=ED=81=AC=EB=A6=AC=EC=97=90=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EA=B4=80=EB=A6=AC=EC=9E=90=20API=20-=20=EC=8B=9C?= =?UTF-8?q?=EB=A6=AC=EC=A6=88=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20API=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CreatorAdminContentSeriesController.kt | 18 ++++++++ .../CreatorAdminContentSeriesRepository.kt | 44 +++++++++++++++++++ .../CreatorAdminContentSeriesService.kt | 17 ++++++- ...etCreatorAdminContentSeriesListResponse.kt | 14 ++++++ 4 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/GetCreatorAdminContentSeriesListResponse.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt index 9f6fc90..cf49e55 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt @@ -3,9 +3,11 @@ package kr.co.vividnext.sodalive.creator.admin.content.series import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.member.Member +import org.springframework.data.domain.Pageable import org.springframework.lang.Nullable import org.springframework.security.access.prepost.PreAuthorize import org.springframework.security.core.annotation.AuthenticationPrincipal +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.RequestMapping @@ -40,4 +42,20 @@ class CreatorAdminContentSeriesController(private val service: CreatorAdminConte ApiResponse.ok(service.modifySeries(image, requestString, member), "시리즈가 수정되었습니다.") } + + @GetMapping + fun getSeriesList( + pageable: Pageable, + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? + ) = run { + if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + + ApiResponse.ok( + service.getSeriesList( + offset = pageable.offset, + limit = pageable.pageSize.toLong(), + creatorId = member.id!! + ) + ) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesRepository.kt index 0c06767..88c3fef 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesRepository.kt @@ -8,6 +8,13 @@ interface CreatorAdminContentSeriesRepository : JpaRepository, Cre interface CreatorAdminContentSeriesQueryRepository { fun findByIdAndCreatorId(id: Long, creatorId: Long): Series? + fun getSeriesCount(creatorId: Long): Int + fun getSeriesList( + offset: Long, + limit: Long, + creatorId: Long, + imageHost: String + ): List } class CreatorAdminContentSeriesQueryRepositoryImpl( @@ -22,4 +29,41 @@ class CreatorAdminContentSeriesQueryRepositoryImpl( ) .fetchFirst() } + + override fun getSeriesCount(creatorId: Long): Int { + return queryFactory + .select(series.id) + .from(series) + .where( + series.member.id.eq(creatorId) + .and(series.isActive.isTrue) + ) + .fetch() + .size + } + + override fun getSeriesList( + offset: Long, + limit: Long, + creatorId: Long, + imageHost: String + ): List { + return queryFactory + .select( + QGetCreatorAdminContentSeriesListItem( + series.id, + series.title, + series.coverImage.prepend("/").prepend(imageHost) + ) + ) + .from(series) + .where( + series.member.id.eq(creatorId) + .and(series.isActive.isTrue) + ) + .orderBy(series.id.desc()) + .offset(offset) + .limit(limit) + .fetch() + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt index 3721d12..8ae0c33 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt @@ -25,7 +25,10 @@ class CreatorAdminContentSeriesService( private val objectMapper: ObjectMapper, @Value("\${cloud.aws.s3.bucket}") - private val coverImageBucket: String + private val coverImageBucket: String, + + @Value("\${cloud.aws.cloud-front.host}") + private val coverImageHost: String ) { @Transactional fun createSeries(coverImage: MultipartFile?, requestString: String, member: Member) { @@ -156,4 +159,16 @@ class CreatorAdminContentSeriesService( series.studio = request.studio } } + + fun getSeriesList(offset: Long, limit: Long, creatorId: Long): GetCreatorAdminContentSeriesListResponse { + val totalCount = repository.getSeriesCount(creatorId = creatorId) + val seriesList = repository.getSeriesList( + offset = offset, + limit = limit, + creatorId = creatorId, + imageHost = coverImageHost + ) + + return GetCreatorAdminContentSeriesListResponse(totalCount, seriesList) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/GetCreatorAdminContentSeriesListResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/GetCreatorAdminContentSeriesListResponse.kt new file mode 100644 index 0000000..da32444 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/GetCreatorAdminContentSeriesListResponse.kt @@ -0,0 +1,14 @@ +package kr.co.vividnext.sodalive.creator.admin.content.series + +import com.querydsl.core.annotations.QueryProjection + +data class GetCreatorAdminContentSeriesListResponse( + val totalCount: Int, + val items: List +) + +data class GetCreatorAdminContentSeriesListItem @QueryProjection constructor( + val seriesId: Long, + val title: String, + val coverImageUrl: String +) From 50f02892e3aa38c524952052d783736d64622021 Mon Sep 17 00:00:00 2001 From: Klaus Date: Wed, 17 Apr 2024 17:04:15 +0900 Subject: [PATCH 05/11] =?UTF-8?q?=ED=81=AC=EB=A6=AC=EC=97=90=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EA=B4=80=EB=A6=AC=EC=9E=90=20API=20-=20=EC=8B=9C?= =?UTF-8?q?=EB=A6=AC=EC=A6=88=20=EC=83=81=EC=84=B8=20API=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CreatorAdminContentSeriesController.kt | 11 ++++++ .../CreatorAdminContentSeriesService.kt | 7 ++++ ...CreatorAdminContentSeriesDetailResponse.kt | 15 +++++++ .../creator/admin/content/series/Series.kt | 39 +++++++++++++++++++ 4 files changed, 72 insertions(+) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/GetCreatorAdminContentSeriesDetailResponse.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt index cf49e55..0282f4b 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt @@ -8,6 +8,7 @@ import org.springframework.lang.Nullable import org.springframework.security.access.prepost.PreAuthorize 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.PostMapping import org.springframework.web.bind.annotation.PutMapping import org.springframework.web.bind.annotation.RequestMapping @@ -58,4 +59,14 @@ class CreatorAdminContentSeriesController(private val service: CreatorAdminConte ) ) } + + @GetMapping("/{id}") + fun getDetail( + @PathVariable id: Long, + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? + ) = run { + if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + + ApiResponse.ok(service.getDetail(id = id, memberId = member.id!!)) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt index 8ae0c33..995a85d 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt @@ -171,4 +171,11 @@ class CreatorAdminContentSeriesService( return GetCreatorAdminContentSeriesListResponse(totalCount, seriesList) } + + fun getDetail(id: Long, memberId: Long): GetCreatorAdminContentSeriesDetailResponse { + val series = repository.findByIdAndCreatorId(id = id, creatorId = memberId) + ?: throw SodaException("잘못된 접근입니다.") + + return series.toDetailResponse(imageHost = coverImageHost) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/GetCreatorAdminContentSeriesDetailResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/GetCreatorAdminContentSeriesDetailResponse.kt new file mode 100644 index 0000000..9cc2271 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/GetCreatorAdminContentSeriesDetailResponse.kt @@ -0,0 +1,15 @@ +package kr.co.vividnext.sodalive.creator.admin.content.series + +data class GetCreatorAdminContentSeriesDetailResponse( + val seriesId: Long, + val title: String, + val introduction: String, + val coverImage: String, + val publishedDaysOfWeek: String, + val genre: String, + val keywords: String, + val isAdult: Boolean, + val state: String, + val writer: String?, + val studio: String? +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/Series.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/Series.kt index 504a180..c801f13 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/Series.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/Series.kt @@ -56,4 +56,43 @@ data class Series( @OneToMany(mappedBy = "series", cascade = [CascadeType.ALL]) var keywordList: MutableList = mutableListOf() + + fun toDetailResponse(imageHost: String): GetCreatorAdminContentSeriesDetailResponse { + return GetCreatorAdminContentSeriesDetailResponse( + seriesId = id!!, + title = title, + introduction = introduction, + coverImage = "$imageHost/$coverImage!!", + publishedDaysOfWeek = publishedDaysOfWeekText(), + genre = genre!!.genre, + keywords = keywordList.map { it.keyword!!.tag }.joinToString(" ") { it }, + isAdult = isAdult, + state = stateSeriesText(), + writer = writer, + studio = studio + ) + } + + private fun publishedDaysOfWeekText(): String { + return publishedDaysOfWeek.toList().sortedBy { it.ordinal }.map { + when (it) { + SeriesPublishedDaysOfWeek.SUN -> "일" + SeriesPublishedDaysOfWeek.MON -> "월" + SeriesPublishedDaysOfWeek.TUE -> "화" + SeriesPublishedDaysOfWeek.WED -> "수" + SeriesPublishedDaysOfWeek.THU -> "목" + SeriesPublishedDaysOfWeek.FRI -> "금" + SeriesPublishedDaysOfWeek.SAT -> "토" + SeriesPublishedDaysOfWeek.RANDOM -> "랜덤" + } + }.joinToString(", ") { it } + } + + private fun stateSeriesText(): String { + return when (state) { + SeriesState.PROCEEDING -> "연재중" + SeriesState.SUSPEND -> "휴재중" + SeriesState.COMPLETE -> "완결" + } + } } From b849de00dca7af01aa3b1a0cb98309055ab934bb Mon Sep 17 00:00:00 2001 From: Klaus Date: Wed, 17 Apr 2024 18:08:40 +0900 Subject: [PATCH 06/11] =?UTF-8?q?=ED=81=AC=EB=A6=AC=EC=97=90=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EA=B4=80=EB=A6=AC=EC=9E=90=20API=20-=20=EC=8B=9C?= =?UTF-8?q?=EB=A6=AC=EC=A6=88=20=EC=BD=98=ED=85=90=EC=B8=A0=20API=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CreatorAdminContentSeriesController.kt | 18 +++++++ .../CreatorAdminContentSeriesRepository.kt | 53 +++++++++++++++++++ .../CreatorAdminContentSeriesService.kt | 17 ++++++ ...reatorAdminContentSeriesContentResponse.kt | 15 ++++++ 4 files changed, 103 insertions(+) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/GetCreatorAdminContentSeriesContentResponse.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt index 0282f4b..2e0c6a0 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt @@ -69,4 +69,22 @@ class CreatorAdminContentSeriesController(private val service: CreatorAdminConte ApiResponse.ok(service.getDetail(id = id, memberId = member.id!!)) } + + @GetMapping("/{id}/content") + fun getSeriesContent( + @PathVariable id: Long, + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, + pageable: Pageable + ) = run { + if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + + ApiResponse.ok( + service.getSeriesContent( + seriesId = id, + offset = pageable.offset, + limit = pageable.pageSize.toLong(), + creatorId = member.id!! + ) + ) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesRepository.kt index 88c3fef..14fd766 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesRepository.kt @@ -1,7 +1,9 @@ package kr.co.vividnext.sodalive.creator.admin.content.series import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.content.QAudioContent.audioContent import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series +import kr.co.vividnext.sodalive.creator.admin.content.series.QSeriesContent.seriesContent import org.springframework.data.jpa.repository.JpaRepository interface CreatorAdminContentSeriesRepository : JpaRepository, CreatorAdminContentSeriesQueryRepository @@ -15,6 +17,14 @@ interface CreatorAdminContentSeriesQueryRepository { creatorId: Long, imageHost: String ): List + + fun getSeriesContentCount(creatorId: Long): Int + fun getSeriesContentList( + offset: Long, + limit: Long, + creatorId: Long, + imageHost: String + ): List } class CreatorAdminContentSeriesQueryRepositoryImpl( @@ -66,4 +76,47 @@ class CreatorAdminContentSeriesQueryRepositoryImpl( .limit(limit) .fetch() } + + override fun getSeriesContentCount(creatorId: Long): Int { + return queryFactory + .select(seriesContent.id) + .from(seriesContent) + .innerJoin(seriesContent.series, series) + .innerJoin(seriesContent.content, audioContent) + .where( + series.member.id.eq(creatorId) + .and(audioContent.member.id.eq(creatorId)) + .and(series.isActive.isTrue) + ) + .fetch() + .size + } + + override fun getSeriesContentList( + offset: Long, + limit: Long, + creatorId: Long, + imageHost: String + ): List { + return queryFactory + .select( + QGetCreatorAdminContentSeriesContentItem( + audioContent.id, + audioContent.coverImage.prepend("/").prepend(imageHost), + audioContent.title, + audioContent.isAdult + ) + ) + .from(seriesContent) + .innerJoin(seriesContent.series, series) + .innerJoin(seriesContent.content, audioContent) + .where( + series.member.id.eq(creatorId) + .and(audioContent.member.id.eq(creatorId)) + .and(series.isActive.isTrue) + ) + .offset(offset) + .limit(limit) + .fetch() + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt index 995a85d..c71e588 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt @@ -178,4 +178,21 @@ class CreatorAdminContentSeriesService( return series.toDetailResponse(imageHost = coverImageHost) } + + fun getSeriesContent( + seriesId: Long, + offset: Long, + limit: Long, + creatorId: Long + ): GetCreatorAdminContentSeriesContentResponse { + val totalCount = repository.getSeriesContentCount(creatorId = creatorId) + val seriesContentList = repository.getSeriesContentList( + offset = offset, + limit = limit, + creatorId = creatorId, + imageHost = coverImageHost + ) + + return GetCreatorAdminContentSeriesContentResponse(totalCount, seriesContentList) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/GetCreatorAdminContentSeriesContentResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/GetCreatorAdminContentSeriesContentResponse.kt new file mode 100644 index 0000000..fd9118f --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/GetCreatorAdminContentSeriesContentResponse.kt @@ -0,0 +1,15 @@ +package kr.co.vividnext.sodalive.creator.admin.content.series + +import com.querydsl.core.annotations.QueryProjection + +data class GetCreatorAdminContentSeriesContentResponse( + val totalCount: Int, + val items: List +) + +data class GetCreatorAdminContentSeriesContentItem @QueryProjection constructor( + val contentId: Long, + val coverImage: String, + val title: String, + val isAdult: Boolean +) From 02e6e77453ea83ec56039ec42972b473790d13d3 Mon Sep 17 00:00:00 2001 From: Klaus Date: Wed, 17 Apr 2024 18:52:10 +0900 Subject: [PATCH 07/11] =?UTF-8?q?=ED=81=AC=EB=A6=AC=EC=97=90=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EA=B4=80=EB=A6=AC=EC=9E=90=20API=20-=20=EA=B8=B0?= =?UTF-8?q?=EC=A1=B4=20=EC=BD=98=ED=85=90=EC=B8=A0=EB=A5=BC=20=EC=8B=9C?= =?UTF-8?q?=EB=A6=AC=EC=A6=88=EC=97=90=20=EC=B6=94=EA=B0=80=ED=95=98?= =?UTF-8?q?=EB=8A=94=20API=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CreatorAdminContentSeriesController.kt | 15 +++++++++++ .../CreatorAdminContentSeriesService.kt | 27 +++++++++++++++++++ .../AddingContentToTheSeriesRequest.kt | 6 +++++ 3 files changed, 48 insertions(+) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/content/AddingContentToTheSeriesRequest.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt index 2e0c6a0..cb1d109 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt @@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.creator.admin.content.series import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.common.SodaException +import kr.co.vividnext.sodalive.creator.admin.content.series.content.AddingContentToTheSeriesRequest import kr.co.vividnext.sodalive.member.Member import org.springframework.data.domain.Pageable import org.springframework.lang.Nullable @@ -11,6 +12,7 @@ import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable 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.RequestPart import org.springframework.web.bind.annotation.RestController @@ -87,4 +89,17 @@ class CreatorAdminContentSeriesController(private val service: CreatorAdminConte ) ) } + + @PostMapping("/add/content") + fun addingContentToTheSeries( + @RequestBody request: AddingContentToTheSeriesRequest, + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? + ) = run { + if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + + ApiResponse.ok( + service.addingContentToTheSeries(request, memberId = member.id!!), + "콘텐츠가 추가되었습니다." + ) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt index c71e588..31a5d18 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt @@ -4,8 +4,10 @@ import com.amazonaws.services.s3.model.ObjectMetadata import com.fasterxml.jackson.databind.ObjectMapper import kr.co.vividnext.sodalive.aws.s3.S3Uploader import kr.co.vividnext.sodalive.common.SodaException +import kr.co.vividnext.sodalive.content.AudioContentRepository import kr.co.vividnext.sodalive.content.hashtag.HashTag import kr.co.vividnext.sodalive.content.hashtag.HashTagRepository +import kr.co.vividnext.sodalive.creator.admin.content.series.content.AddingContentToTheSeriesRequest import kr.co.vividnext.sodalive.creator.admin.content.series.genre.CreatorAdminContentSeriesGenreRepository import kr.co.vividnext.sodalive.creator.admin.content.series.keyword.SeriesKeyword import kr.co.vividnext.sodalive.member.Member @@ -20,6 +22,7 @@ class CreatorAdminContentSeriesService( private val repository: CreatorAdminContentSeriesRepository, private val genreRepository: CreatorAdminContentSeriesGenreRepository, private val hashTagRepository: HashTagRepository, + private val audioContentRepository: AudioContentRepository, private val s3Uploader: S3Uploader, private val objectMapper: ObjectMapper, @@ -195,4 +198,28 @@ class CreatorAdminContentSeriesService( return GetCreatorAdminContentSeriesContentResponse(totalCount, seriesContentList) } + + @Transactional + fun addingContentToTheSeries(request: AddingContentToTheSeriesRequest, memberId: Long) { + val series = repository.findByIdAndCreatorId(id = request.seriesId, creatorId = memberId) + ?: throw SodaException("잘못된 접근입니다.") + + val seriesContentList = mutableListOf() + + for (contentId in request.contentIdList) { + val content = audioContentRepository.findByIdAndCreatorId(contentId = contentId, creatorId = memberId) + ?: continue + + val seriesContent = SeriesContent() + seriesContent.series = series + seriesContent.content = content + seriesContentList.add(seriesContent) + } + + if (seriesContentList.size > 0) { + series.contentList.addAll(seriesContentList) + } else { + throw SodaException("추가된 콘텐츠가 없습니다.") + } + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/content/AddingContentToTheSeriesRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/content/AddingContentToTheSeriesRequest.kt new file mode 100644 index 0000000..b9a9525 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/content/AddingContentToTheSeriesRequest.kt @@ -0,0 +1,6 @@ +package kr.co.vividnext.sodalive.creator.admin.content.series.content + +data class AddingContentToTheSeriesRequest( + val seriesId: Long, + val contentIdList: List +) From 0185c09d55e5ea607ae6f918a6f0975a96e73fdc Mon Sep 17 00:00:00 2001 From: Klaus Date: Wed, 17 Apr 2024 19:18:36 +0900 Subject: [PATCH 08/11] =?UTF-8?q?=ED=81=AC=EB=A6=AC=EC=97=90=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EA=B4=80=EB=A6=AC=EC=9E=90=20API=20-=20=EC=8B=9C?= =?UTF-8?q?=EB=A6=AC=EC=A6=88=20=EC=BD=98=ED=85=90=EC=B8=A0=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20API=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../series/CreatorAdminContentSeriesController.kt | 14 ++++++++++++++ .../series/CreatorAdminContentSeriesService.kt | 9 +++++++++ .../content/RemoveContentToTheSeriesRequest.kt | 3 +++ 3 files changed, 26 insertions(+) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/content/RemoveContentToTheSeriesRequest.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt index cb1d109..ce1adfd 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesController.kt @@ -3,6 +3,7 @@ package kr.co.vividnext.sodalive.creator.admin.content.series import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.creator.admin.content.series.content.AddingContentToTheSeriesRequest +import kr.co.vividnext.sodalive.creator.admin.content.series.content.RemoveContentToTheSeriesRequest import kr.co.vividnext.sodalive.member.Member import org.springframework.data.domain.Pageable import org.springframework.lang.Nullable @@ -102,4 +103,17 @@ class CreatorAdminContentSeriesController(private val service: CreatorAdminConte "콘텐츠가 추가되었습니다." ) } + + @PutMapping("/remove/content") + fun removeContentInTheSeries( + @RequestBody request: RemoveContentToTheSeriesRequest, + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? + ) = run { + if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + + ApiResponse.ok( + service.removeContentInTheSeries(request, memberId = member.id!!), + "콘텐츠를 삭제하였습니다." + ) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt index 31a5d18..d000fe4 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/CreatorAdminContentSeriesService.kt @@ -8,6 +8,7 @@ import kr.co.vividnext.sodalive.content.AudioContentRepository import kr.co.vividnext.sodalive.content.hashtag.HashTag import kr.co.vividnext.sodalive.content.hashtag.HashTagRepository import kr.co.vividnext.sodalive.creator.admin.content.series.content.AddingContentToTheSeriesRequest +import kr.co.vividnext.sodalive.creator.admin.content.series.content.RemoveContentToTheSeriesRequest import kr.co.vividnext.sodalive.creator.admin.content.series.genre.CreatorAdminContentSeriesGenreRepository import kr.co.vividnext.sodalive.creator.admin.content.series.keyword.SeriesKeyword import kr.co.vividnext.sodalive.member.Member @@ -222,4 +223,12 @@ class CreatorAdminContentSeriesService( throw SodaException("추가된 콘텐츠가 없습니다.") } } + + @Transactional + fun removeContentInTheSeries(request: RemoveContentToTheSeriesRequest, memberId: Long) { + val series = repository.findByIdAndCreatorId(id = request.seriesId, creatorId = memberId) + ?: throw SodaException("잘못된 접근입니다.") + + series.contentList.removeIf { it.content!!.id == request.contentId } + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/content/RemoveContentToTheSeriesRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/content/RemoveContentToTheSeriesRequest.kt new file mode 100644 index 0000000..42d19dc --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/series/content/RemoveContentToTheSeriesRequest.kt @@ -0,0 +1,3 @@ +package kr.co.vividnext.sodalive.creator.admin.content.series.content + +data class RemoveContentToTheSeriesRequest(val seriesId: Long, val contentId: Long) From 0bf2b1b4ae7595d660eab2ab8c7d91296556bd6d Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 18 Apr 2024 12:49:15 +0900 Subject: [PATCH 09/11] =?UTF-8?q?=EA=B4=80=EB=A6=AC=EC=9E=90=20API=20-=20?= =?UTF-8?q?=EC=8B=9C=EB=A6=AC=EC=A6=88=20=EC=9E=A5=EB=A5=B4=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=A1=B0=ED=9A=8C=20API=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AdminContentSeriesGenreController.kt | 4 ++++ .../AdminContentSeriesGenreRepository.kt | 19 +++++++++++++++++-- .../genre/AdminContentSeriesGenreService.kt | 4 ++++ .../genre/GetSeriesGenreListResponse.kt | 9 +++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/GetSeriesGenreListResponse.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreController.kt index b0dd061..6963e74 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreController.kt @@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.admin.content.series.genre 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 @@ -27,4 +28,7 @@ class AdminContentSeriesGenreController(private val service: AdminContentSeriesG service.modifySeriesGenreOrders(ids = request.ids), "수정되었습니다." ) + + @GetMapping + fun getSeriesGenreList() = ApiResponse.ok(service.getSeriesGenreList()) } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreRepository.kt index b91ffa5..3587baa 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreRepository.kt @@ -1,9 +1,24 @@ package kr.co.vividnext.sodalive.admin.content.series.genre +import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.admin.content.series.genre.QSeriesGenre.seriesGenre import org.springframework.data.jpa.repository.JpaRepository interface AdminContentSeriesGenreRepository : JpaRepository, AdminContentSeriesGenreQueryRepository -interface AdminContentSeriesGenreQueryRepository +interface AdminContentSeriesGenreQueryRepository { + fun getSeriesGenreList(): List +} -class AdminContentSeriesGenreQueryRepositoryImpl : AdminContentSeriesGenreQueryRepository +class AdminContentSeriesGenreQueryRepositoryImpl( + private val queryFactory: JPAQueryFactory +) : AdminContentSeriesGenreQueryRepository { + override fun getSeriesGenreList(): List { + return queryFactory + .select(QGetSeriesGenreListResponse(seriesGenre.id, seriesGenre.genre, seriesGenre.isAdult)) + .from(seriesGenre) + .where(seriesGenre.isActive.isTrue) + .orderBy(seriesGenre.orders.asc()) + .fetch() + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreService.kt index 058b782..30cb1b2 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreService.kt @@ -46,4 +46,8 @@ class AdminContentSeriesGenreService(private val repository: AdminContentSeriesG } } } + + fun getSeriesGenreList(): List { + return repository.getSeriesGenreList() + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/GetSeriesGenreListResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/GetSeriesGenreListResponse.kt new file mode 100644 index 0000000..d642c47 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/GetSeriesGenreListResponse.kt @@ -0,0 +1,9 @@ +package kr.co.vividnext.sodalive.admin.content.series.genre + +import com.querydsl.core.annotations.QueryProjection + +data class GetSeriesGenreListResponse @QueryProjection constructor( + val id: Long, + val genre: String, + val isAdult: Boolean +) From 4512aed44a3636104aff258674d7283b631ed51a Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 18 Apr 2024 13:33:39 +0900 Subject: [PATCH 10/11] =?UTF-8?q?=EA=B4=80=EB=A6=AC=EC=9E=90=20API=20-=20?= =?UTF-8?q?=EC=8B=9C=EB=A6=AC=EC=A6=88=20=EC=9E=A5=EB=A5=B4=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20API=20-=20=EC=97=B0=EB=A0=B9=EC=A0=9C=ED=95=9C=20?= =?UTF-8?q?=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../content/series/genre/AdminContentSeriesGenreController.kt | 2 +- .../content/series/genre/AdminContentSeriesGenreService.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreController.kt index 6963e74..981d4a6 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreController.kt @@ -15,7 +15,7 @@ import org.springframework.web.bind.annotation.RestController class AdminContentSeriesGenreController(private val service: AdminContentSeriesGenreService) { @PostMapping fun createSeriesGenre(@RequestBody request: CreateSeriesGenreRequest) = - ApiResponse.ok(service.createSeriesGenre(genre = request.genre), "생성되었습니다.") + ApiResponse.ok(service.createSeriesGenre(genre = request.genre, isAdult = request.isAdult), "생성되었습니다.") @PutMapping fun modifySeriesGenre(@RequestBody request: ModifySeriesGenreRequest) = ApiResponse.ok( diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreService.kt index 30cb1b2..6f107ee 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreService.kt @@ -9,8 +9,8 @@ import org.springframework.transaction.annotation.Transactional class AdminContentSeriesGenreService(private val repository: AdminContentSeriesGenreRepository) { @Transactional - fun createSeriesGenre(genre: String) { - val seriesGenre = SeriesGenre(genre = genre.trim()) + fun createSeriesGenre(genre: String, isAdult: Boolean) { + val seriesGenre = SeriesGenre(genre = genre.trim(), isAdult = isAdult) repository.save(seriesGenre) } From 69b46d791f806544a3b2c2e7f52abb84e5dc6035 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 18 Apr 2024 13:35:28 +0900 Subject: [PATCH 11/11] =?UTF-8?q?=EA=B4=80=EB=A6=AC=EC=9E=90=20API=20-=20?= =?UTF-8?q?=EC=8B=9C=EB=A6=AC=EC=A6=88=20=EC=9E=A5=EB=A5=B4=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20API=20-=20=EC=8B=9C=EB=A6=AC=EC=A6=88=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=EC=8B=9C=20genre=EC=97=90=20=5Fdeleted=20=EC=A0=91?= =?UTF-8?q?=EB=AF=B8=EC=82=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../content/series/genre/AdminContentSeriesGenreService.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreService.kt index 6f107ee..68eb284 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreService.kt @@ -33,6 +33,10 @@ class AdminContentSeriesGenreService(private val repository: AdminContentSeriesG if (request.isActive != null) { seriesGenre.isActive = request.isActive + + if (!request.isActive) { + seriesGenre.genre = "${seriesGenre.genre}_deleted" + } } }