diff --git a/src/main/kotlin/com/dotori/v2/domain/music/domain/entity/Music.kt b/src/main/kotlin/com/dotori/v2/domain/music/domain/entity/Music.kt index 0929b3d6..e9eba35e 100644 --- a/src/main/kotlin/com/dotori/v2/domain/music/domain/entity/Music.kt +++ b/src/main/kotlin/com/dotori/v2/domain/music/domain/entity/Music.kt @@ -22,9 +22,18 @@ class Music( val thumbnail: String, @Column(name = "like_count", nullable = false, columnDefinition = "INT default 0") - val likeCount: Int = 0, + var likeCount: Int = 0, @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id", nullable = false) val member: Member -) : BaseTimeEntity() \ No newline at end of file +) : BaseTimeEntity() { + + fun plusLikeCount() { + this.likeCount++ + } + + fun minusLikeCount() { + this.likeCount-- + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/dotori/v2/domain/music/domain/entity/MusicLike.kt b/src/main/kotlin/com/dotori/v2/domain/music/domain/entity/MusicLike.kt new file mode 100644 index 00000000..65de1edb --- /dev/null +++ b/src/main/kotlin/com/dotori/v2/domain/music/domain/entity/MusicLike.kt @@ -0,0 +1,25 @@ +package com.dotori.v2.domain.music.domain.entity + +import javax.persistence.Column +import javax.persistence.Entity +import javax.persistence.GeneratedValue +import javax.persistence.GenerationType +import javax.persistence.Id +import javax.persistence.Table + +@Entity +@Table(name = "likes") +class MusicLike ( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "like_id") + val id: Long = 0, + + @Column(name = "music_id", nullable = false) + val musicId: Long, + + @Column(name = "member_id", nullable = false) + val memberId: Long, +) { + +} \ No newline at end of file diff --git a/src/main/kotlin/com/dotori/v2/domain/music/domain/repository/MusicLikeRepository.kt b/src/main/kotlin/com/dotori/v2/domain/music/domain/repository/MusicLikeRepository.kt new file mode 100644 index 00000000..520fdf57 --- /dev/null +++ b/src/main/kotlin/com/dotori/v2/domain/music/domain/repository/MusicLikeRepository.kt @@ -0,0 +1,16 @@ +package com.dotori.v2.domain.music.domain.repository + +import com.dotori.v2.domain.music.domain.entity.MusicLike +import io.lettuce.core.dynamic.annotation.Param +import javax.persistence.LockModeType +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Lock +import org.springframework.data.jpa.repository.Query + +interface MusicLikeRepository : JpaRepository { + @Lock(LockModeType.PESSIMISTIC_WRITE) + @Query("select l from MusicLike l where l.memberId = :memberId and l.musicId = :musicId") + fun findByMemberIdAndMusicId(@Param("memberId") memberId: Long,@Param("musicId") musicId: Long): MusicLike? + + fun existsByMusicIdAndMemberId(musicId: Long, memberId: Long): Boolean +} \ No newline at end of file diff --git a/src/main/kotlin/com/dotori/v2/domain/music/domain/repository/MusicRepository.kt b/src/main/kotlin/com/dotori/v2/domain/music/domain/repository/MusicRepository.kt index c6d25593..17f30ebc 100644 --- a/src/main/kotlin/com/dotori/v2/domain/music/domain/repository/MusicRepository.kt +++ b/src/main/kotlin/com/dotori/v2/domain/music/domain/repository/MusicRepository.kt @@ -18,4 +18,6 @@ interface MusicRepository : JpaRepository { fun deleteAllByCreatedDateBefore(date: LocalDateTime) + @Query(value = "select * from music where created_date like :date% order by music.like_count desc", nativeQuery = true) + fun findAllByCreatedDateOrderByLikeCountDESC(@Param("date") date: LocalDate): List } \ No newline at end of file diff --git a/src/main/kotlin/com/dotori/v2/domain/music/exception/NotValidMusicLikeException.kt b/src/main/kotlin/com/dotori/v2/domain/music/exception/NotValidMusicLikeException.kt new file mode 100644 index 00000000..250dbe01 --- /dev/null +++ b/src/main/kotlin/com/dotori/v2/domain/music/exception/NotValidMusicLikeException.kt @@ -0,0 +1,7 @@ +package com.dotori.v2.domain.music.exception + +import com.dotori.v2.global.error.ErrorCode +import com.dotori.v2.global.error.exception.BasicException + +class NotValidMusicLikeException : BasicException(ErrorCode.NOT_VALID_MUSIC_LIKE) { +} \ No newline at end of file diff --git a/src/main/kotlin/com/dotori/v2/domain/music/presentation/admin/AdminMusicController.kt b/src/main/kotlin/com/dotori/v2/domain/music/presentation/admin/AdminMusicController.kt index ee56e20f..d21f8f83 100644 --- a/src/main/kotlin/com/dotori/v2/domain/music/presentation/admin/AdminMusicController.kt +++ b/src/main/kotlin/com/dotori/v2/domain/music/presentation/admin/AdminMusicController.kt @@ -1,8 +1,12 @@ package com.dotori.v2.domain.music.presentation.admin +import com.dotori.v2.domain.music.presentation.data.res.MusicLikeCountResDto import com.dotori.v2.domain.music.presentation.data.res.MusicListResDto +import com.dotori.v2.domain.music.presentation.data.res.MusicRankListResDto import com.dotori.v2.domain.music.service.DeleteMusicService +import com.dotori.v2.domain.music.service.FindMusicRankService import com.dotori.v2.domain.music.service.FindMusicsService +import com.dotori.v2.domain.music.service.ToggleMusicLikeService import org.springframework.format.annotation.DateTimeFormat import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity @@ -13,7 +17,9 @@ import java.time.LocalDate @RequestMapping("/v2/admin/music") class AdminMusicController( private val findMusicsService: FindMusicsService, - private val deleteMusicService: DeleteMusicService + private val deleteMusicService: DeleteMusicService, + private val toggleMusicLikeService: ToggleMusicLikeService, + private val findMusicRankService: FindMusicRankService ) { @GetMapping fun findMusics( @@ -29,4 +35,16 @@ class AdminMusicController( deleteMusicService.execute(musicId) .run { ResponseEntity.status(HttpStatus.NO_CONTENT).build() } + @PatchMapping("/{music_id}/like") + fun toggleMusicLike(@PathVariable("music_id") musicId: Long): ResponseEntity = + ResponseEntity.status(HttpStatus.OK).body(toggleMusicLikeService.execute(musicId)) + + @GetMapping("/like") + fun findMusicRank( + @RequestParam( + value = "date", + required = true + ) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) date: LocalDate + ): ResponseEntity = + ResponseEntity.status(HttpStatus.OK).body(findMusicRankService.execute(date)) } \ No newline at end of file diff --git a/src/main/kotlin/com/dotori/v2/domain/music/presentation/councillor/CouncillorMusicController.kt b/src/main/kotlin/com/dotori/v2/domain/music/presentation/councillor/CouncillorMusicController.kt index af3cc266..5ec7e659 100644 --- a/src/main/kotlin/com/dotori/v2/domain/music/presentation/councillor/CouncillorMusicController.kt +++ b/src/main/kotlin/com/dotori/v2/domain/music/presentation/councillor/CouncillorMusicController.kt @@ -1,10 +1,14 @@ package com.dotori.v2.domain.music.presentation.councillor import com.dotori.v2.domain.music.presentation.data.req.ApplyMusicReqDto +import com.dotori.v2.domain.music.presentation.data.res.MusicLikeCountResDto import com.dotori.v2.domain.music.presentation.data.res.MusicListResDto +import com.dotori.v2.domain.music.presentation.data.res.MusicRankListResDto import com.dotori.v2.domain.music.service.ApplyMusicService import com.dotori.v2.domain.music.service.DeleteMusicService +import com.dotori.v2.domain.music.service.FindMusicRankService import com.dotori.v2.domain.music.service.FindMusicsService +import com.dotori.v2.domain.music.service.ToggleMusicLikeService import org.springframework.format.annotation.DateTimeFormat import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity @@ -17,7 +21,9 @@ import java.time.LocalDateTime class CouncillorMusicController( private val applyMusicService: ApplyMusicService, private val findMusicsService: FindMusicsService, - private val deleteMusicService: DeleteMusicService + private val deleteMusicService: DeleteMusicService, + private val toggleMusicLikeService: ToggleMusicLikeService, + private val findMusicRankService: FindMusicRankService ) { @PostMapping fun applyMusic(@RequestBody applyMusicReqDto: ApplyMusicReqDto): ResponseEntity = @@ -37,4 +43,17 @@ class CouncillorMusicController( fun deleteMusic(@PathVariable("music_id") musicId: Long): ResponseEntity = deleteMusicService.execute(musicId) .run { ResponseEntity.status(HttpStatus.NO_CONTENT).build() } + + @PatchMapping("/{music_id}/like") + fun toggleMusicLike(@PathVariable("music_id") musicId: Long): ResponseEntity = + ResponseEntity.status(HttpStatus.OK).body(toggleMusicLikeService.execute(musicId)) + + @GetMapping("/like") + fun findMusicRank( + @RequestParam( + value = "date", + required = true + ) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) date: LocalDate + ): ResponseEntity = + ResponseEntity.status(HttpStatus.OK).body(findMusicRankService.execute(date)) } \ No newline at end of file diff --git a/src/main/kotlin/com/dotori/v2/domain/music/presentation/data/res/MusicLikeCountResDto.kt b/src/main/kotlin/com/dotori/v2/domain/music/presentation/data/res/MusicLikeCountResDto.kt new file mode 100644 index 00000000..75471a4f --- /dev/null +++ b/src/main/kotlin/com/dotori/v2/domain/music/presentation/data/res/MusicLikeCountResDto.kt @@ -0,0 +1,5 @@ +package com.dotori.v2.domain.music.presentation.data.res + +data class MusicLikeCountResDto( + val likeCount: Int +) diff --git a/src/main/kotlin/com/dotori/v2/domain/music/presentation/data/res/MusicRankListResDto.kt b/src/main/kotlin/com/dotori/v2/domain/music/presentation/data/res/MusicRankListResDto.kt new file mode 100644 index 00000000..f7b0cb61 --- /dev/null +++ b/src/main/kotlin/com/dotori/v2/domain/music/presentation/data/res/MusicRankListResDto.kt @@ -0,0 +1,5 @@ +package com.dotori.v2.domain.music.presentation.data.res + +data class MusicRankListResDto( + val content: List +) diff --git a/src/main/kotlin/com/dotori/v2/domain/music/presentation/data/res/MusicRankResDto.kt b/src/main/kotlin/com/dotori/v2/domain/music/presentation/data/res/MusicRankResDto.kt new file mode 100644 index 00000000..0d444832 --- /dev/null +++ b/src/main/kotlin/com/dotori/v2/domain/music/presentation/data/res/MusicRankResDto.kt @@ -0,0 +1,17 @@ +package com.dotori.v2.domain.music.presentation.data.res + +import java.time.LocalDateTime + +data class MusicRankResDto( + val id: Long, + val rank: Int, + val url: String, + val title: String, + val thumbnail: String, + val username: String, + val email: String, + val createdTime: LocalDateTime, + val stuNum: String, + val likeCount: Int, + val memberLikeCheck: Boolean +) diff --git a/src/main/kotlin/com/dotori/v2/domain/music/presentation/data/res/MusicResDto.kt b/src/main/kotlin/com/dotori/v2/domain/music/presentation/data/res/MusicResDto.kt index bdb70b12..859b1ff6 100644 --- a/src/main/kotlin/com/dotori/v2/domain/music/presentation/data/res/MusicResDto.kt +++ b/src/main/kotlin/com/dotori/v2/domain/music/presentation/data/res/MusicResDto.kt @@ -10,5 +10,7 @@ data class MusicResDto( val username: String, val email: String, val createdTime: LocalDateTime, - val stuNum: String + val stuNum: String, + val likeCount: Int, + val memberLikeCheck: Boolean ) \ No newline at end of file diff --git a/src/main/kotlin/com/dotori/v2/domain/music/presentation/developer/DeveloperMusicController.kt b/src/main/kotlin/com/dotori/v2/domain/music/presentation/developer/DeveloperMusicController.kt index 4eed30dd..af713580 100644 --- a/src/main/kotlin/com/dotori/v2/domain/music/presentation/developer/DeveloperMusicController.kt +++ b/src/main/kotlin/com/dotori/v2/domain/music/presentation/developer/DeveloperMusicController.kt @@ -1,10 +1,14 @@ package com.dotori.v2.domain.music.presentation.developer import com.dotori.v2.domain.music.presentation.data.req.ApplyMusicReqDto +import com.dotori.v2.domain.music.presentation.data.res.MusicLikeCountResDto import com.dotori.v2.domain.music.presentation.data.res.MusicListResDto +import com.dotori.v2.domain.music.presentation.data.res.MusicRankListResDto import com.dotori.v2.domain.music.service.ApplyMusicService import com.dotori.v2.domain.music.service.DeleteMusicService +import com.dotori.v2.domain.music.service.FindMusicRankService import com.dotori.v2.domain.music.service.FindMusicsService +import com.dotori.v2.domain.music.service.ToggleMusicLikeService import org.springframework.format.annotation.DateTimeFormat import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity @@ -17,7 +21,9 @@ import java.time.LocalDateTime class DeveloperMusicController( private val applyMusicService: ApplyMusicService, private val findMusicsService: FindMusicsService, - private val deleteMusicService: DeleteMusicService + private val deleteMusicService: DeleteMusicService, + private val toggleMusicLikeService: ToggleMusicLikeService, + private val findMusicRankService: FindMusicRankService ) { @PostMapping fun applyMusic(@RequestBody applyMusicReqDto: ApplyMusicReqDto): ResponseEntity = @@ -37,4 +43,17 @@ class DeveloperMusicController( fun deleteMusic(@PathVariable("music_id") musicId: Long): ResponseEntity = deleteMusicService.execute(musicId) .run { ResponseEntity.status(HttpStatus.NO_CONTENT).build() } + + @PatchMapping("/{music_id}/like") + fun toggleMusicLike(@PathVariable("music_id") musicId: Long): ResponseEntity = + ResponseEntity.status(HttpStatus.OK).body(toggleMusicLikeService.execute(musicId)) + + @GetMapping("/like") + fun findMusicRank( + @RequestParam( + value = "date", + required = true + ) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) date: LocalDate + ): ResponseEntity = + ResponseEntity.status(HttpStatus.OK).body(findMusicRankService.execute(date)) } \ No newline at end of file diff --git a/src/main/kotlin/com/dotori/v2/domain/music/presentation/member/MemberMusicController.kt b/src/main/kotlin/com/dotori/v2/domain/music/presentation/member/MemberMusicController.kt index fc9cb3c1..5c6d768c 100644 --- a/src/main/kotlin/com/dotori/v2/domain/music/presentation/member/MemberMusicController.kt +++ b/src/main/kotlin/com/dotori/v2/domain/music/presentation/member/MemberMusicController.kt @@ -1,10 +1,14 @@ package com.dotori.v2.domain.music.presentation.member import com.dotori.v2.domain.music.presentation.data.req.ApplyMusicReqDto +import com.dotori.v2.domain.music.presentation.data.res.MusicLikeCountResDto import com.dotori.v2.domain.music.presentation.data.res.MusicListResDto +import com.dotori.v2.domain.music.presentation.data.res.MusicRankListResDto import com.dotori.v2.domain.music.service.ApplyMusicService import com.dotori.v2.domain.music.service.DeleteMyMusicService +import com.dotori.v2.domain.music.service.FindMusicRankService import com.dotori.v2.domain.music.service.FindMusicsService +import com.dotori.v2.domain.music.service.ToggleMusicLikeService import org.springframework.format.annotation.DateTimeFormat import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity @@ -17,7 +21,9 @@ import java.time.LocalDateTime class MemberMusicController( private val applyMusicService: ApplyMusicService, private val findMusicsService: FindMusicsService, - private val deleteMyMusicService: DeleteMyMusicService + private val deleteMyMusicService: DeleteMyMusicService, + private val toggleMusicLikeService: ToggleMusicLikeService, + private val findMusicRankService: FindMusicRankService ) { @PostMapping fun applyMusic(@RequestBody applyMusicReqDto: ApplyMusicReqDto): ResponseEntity = @@ -37,4 +43,17 @@ class MemberMusicController( fun deleteMusic(@PathVariable("music_id") musicId: Long): ResponseEntity = deleteMyMusicService.execute(musicId) .run { ResponseEntity.status(HttpStatus.NO_CONTENT).build() } + + @PatchMapping("/{music_id}/like") + fun toggleMusicLike(@PathVariable("music_id") musicId: Long): ResponseEntity = + ResponseEntity.status(HttpStatus.OK).body(toggleMusicLikeService.execute(musicId)) + + @GetMapping("/like") + fun findMusicRank( + @RequestParam( + value = "date", + required = true + ) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) date: LocalDate + ): ResponseEntity = + ResponseEntity.status(HttpStatus.OK).body(findMusicRankService.execute(date)) } \ No newline at end of file diff --git a/src/main/kotlin/com/dotori/v2/domain/music/service/FindMusicRankService.kt b/src/main/kotlin/com/dotori/v2/domain/music/service/FindMusicRankService.kt new file mode 100644 index 00000000..4b1a2855 --- /dev/null +++ b/src/main/kotlin/com/dotori/v2/domain/music/service/FindMusicRankService.kt @@ -0,0 +1,8 @@ +package com.dotori.v2.domain.music.service + +import com.dotori.v2.domain.music.presentation.data.res.MusicRankListResDto +import java.time.LocalDate + +interface FindMusicRankService { + fun execute(date: LocalDate): MusicRankListResDto +} \ No newline at end of file diff --git a/src/main/kotlin/com/dotori/v2/domain/music/service/ToggleMusicLikeService.kt b/src/main/kotlin/com/dotori/v2/domain/music/service/ToggleMusicLikeService.kt new file mode 100644 index 00000000..ffa818c9 --- /dev/null +++ b/src/main/kotlin/com/dotori/v2/domain/music/service/ToggleMusicLikeService.kt @@ -0,0 +1,7 @@ +package com.dotori.v2.domain.music.service + +import com.dotori.v2.domain.music.presentation.data.res.MusicLikeCountResDto + +interface ToggleMusicLikeService { + fun execute(musicId: Long) : MusicLikeCountResDto +} \ No newline at end of file diff --git a/src/main/kotlin/com/dotori/v2/domain/music/service/impl/ApplyMusicServiceImpl.kt b/src/main/kotlin/com/dotori/v2/domain/music/service/impl/ApplyMusicServiceImpl.kt index 44efa0d4..c0dfbafc 100644 --- a/src/main/kotlin/com/dotori/v2/domain/music/service/impl/ApplyMusicServiceImpl.kt +++ b/src/main/kotlin/com/dotori/v2/domain/music/service/impl/ApplyMusicServiceImpl.kt @@ -60,7 +60,9 @@ class ApplyMusicServiceImpl( username = music.member.memberName, email = music.member.email, createdTime = music.createdDate, - stuNum = music.member.stuNum + stuNum = music.member.stuNum, + likeCount = music.likeCount, + memberLikeCheck = false ) private fun validDayOfWeek(dayOfWeek: DayOfWeek) { diff --git a/src/main/kotlin/com/dotori/v2/domain/music/service/impl/FindMusicRankServiceImpl.kt b/src/main/kotlin/com/dotori/v2/domain/music/service/impl/FindMusicRankServiceImpl.kt new file mode 100644 index 00000000..56410186 --- /dev/null +++ b/src/main/kotlin/com/dotori/v2/domain/music/service/impl/FindMusicRankServiceImpl.kt @@ -0,0 +1,51 @@ +package com.dotori.v2.domain.music.service.impl + +import com.dotori.v2.domain.music.domain.entity.Music +import com.dotori.v2.domain.music.domain.repository.MusicLikeRepository +import com.dotori.v2.domain.music.domain.repository.MusicRepository +import com.dotori.v2.domain.music.presentation.data.res.MusicRankListResDto +import com.dotori.v2.domain.music.presentation.data.res.MusicRankResDto +import com.dotori.v2.domain.music.service.FindMusicRankService +import com.dotori.v2.global.util.UserUtil +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import java.time.LocalDate + +@Service +@Transactional +class FindMusicRankServiceImpl( + private val musicRepository: MusicRepository, + private val musicLikeRepository: MusicLikeRepository, + private val userUtil: UserUtil +) : FindMusicRankService { + + override fun execute(date: LocalDate): MusicRankListResDto { + val responses = musicRepository.findAllByCreatedDateOrderByLikeCountDESC(date) + .mapIndexed { index, music -> + MusicRankResDto( + id = music.id, + rank = index + 1, + url = music.url, + title = music.title, + thumbnail = music.thumbnail, + username = music.member.memberName, + email = music.member.email, + createdTime = music.createdDate, + stuNum = music.member.stuNum, + likeCount = music.likeCount, + memberLikeCheck = checkLike(music) + ) + } + + return MusicRankListResDto( + responses + ) + } + + private fun checkLike(music: Music): Boolean { + val member = userUtil.fetchCurrentUser() + + return musicLikeRepository.existsByMusicIdAndMemberId(music.id, member.id) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/dotori/v2/domain/music/service/impl/FindMusicsServiceImpl.kt b/src/main/kotlin/com/dotori/v2/domain/music/service/impl/FindMusicsServiceImpl.kt index fe8fe614..a82b12d8 100644 --- a/src/main/kotlin/com/dotori/v2/domain/music/service/impl/FindMusicsServiceImpl.kt +++ b/src/main/kotlin/com/dotori/v2/domain/music/service/impl/FindMusicsServiceImpl.kt @@ -1,11 +1,13 @@ package com.dotori.v2.domain.music.service.impl import com.dotori.v2.domain.music.domain.entity.Music +import com.dotori.v2.domain.music.domain.repository.MusicLikeRepository import com.dotori.v2.domain.music.domain.repository.MusicRepository import com.dotori.v2.domain.music.presentation.data.res.MusicListResDto import com.dotori.v2.domain.music.presentation.data.res.MusicResDto import com.dotori.v2.domain.music.service.FindMusicsService import com.dotori.v2.global.config.redis.service.RedisCacheService +import com.dotori.v2.global.util.UserUtil import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import java.time.LocalDate @@ -15,7 +17,9 @@ import java.time.LocalDateTime @Transactional(readOnly = true, rollbackFor = [Exception::class]) class FindMusicsServiceImpl( private val musicRepository: MusicRepository, - private val redisCacheService: RedisCacheService + private val redisCacheService: RedisCacheService, + private val userUtil: UserUtil, + private val musicLikeRepository: MusicLikeRepository ) : FindMusicsService { override fun execute(date: LocalDate): MusicListResDto { @@ -50,6 +54,14 @@ class FindMusicsServiceImpl( username = music.member.memberName, email = music.member.email, createdTime = music.createdDate, - stuNum = music.member.stuNum + stuNum = music.member.stuNum, + likeCount = music.likeCount, + memberLikeCheck = checkLike(music) ) + + private fun checkLike(music: Music): Boolean { + val member = userUtil.fetchCurrentUser() + + return musicLikeRepository.existsByMusicIdAndMemberId(music.id, member.id) + } } \ No newline at end of file diff --git a/src/main/kotlin/com/dotori/v2/domain/music/service/impl/ToggleMusicLikeServiceImpl.kt b/src/main/kotlin/com/dotori/v2/domain/music/service/impl/ToggleMusicLikeServiceImpl.kt new file mode 100644 index 00000000..21e6a212 --- /dev/null +++ b/src/main/kotlin/com/dotori/v2/domain/music/service/impl/ToggleMusicLikeServiceImpl.kt @@ -0,0 +1,67 @@ +package com.dotori.v2.domain.music.service.impl + +import com.dotori.v2.domain.member.domain.entity.Member +import com.dotori.v2.domain.music.domain.entity.Music +import com.dotori.v2.domain.music.domain.entity.MusicLike +import com.dotori.v2.domain.music.domain.repository.MusicLikeRepository +import com.dotori.v2.domain.music.domain.repository.MusicRepository +import com.dotori.v2.domain.music.exception.MusicNotFoundException +import com.dotori.v2.domain.music.presentation.data.res.MusicLikeCountResDto +import com.dotori.v2.domain.music.service.ToggleMusicLikeService +import com.dotori.v2.global.config.redis.service.RedisCacheService +import com.dotori.v2.global.util.UserUtil +import org.springframework.transaction.annotation.Transactional +import org.springframework.data.repository.findByIdOrNull +import org.springframework.stereotype.Service + +@Service +@Transactional(readOnly = false, rollbackFor = [Exception::class]) +class ToggleMusicLikeServiceImpl( + private val musicLikeRepository: MusicLikeRepository, + private val musicRepository: MusicRepository, + private val userUtil: UserUtil, + private val redisCacheService: RedisCacheService +): ToggleMusicLikeService { + override fun execute(musicId: Long) : MusicLikeCountResDto { + val member = userUtil.fetchCurrentUser() + val music: Music = musicRepository.findByIdOrNull(musicId) ?: throw MusicNotFoundException() + + updateLike(music, member) + + return MusicLikeCountResDto ( + likeCount = music.likeCount + ) + } + + private fun updateLike(music: Music, member: Member) { + val like = musicLikeRepository.findByMemberIdAndMusicId(member.id, music.id) + + if (like == null) { + saveLike(music, member) + } else { + deleteLike(like.id, music) + } + } + + private fun saveLike(music: Music, member: Member) { + val musicLike = MusicLike ( + musicId = music.id, + memberId = member.id + ) + musicLikeRepository.save(musicLike) + music.plusLikeCount() + updateCache(music) + } + + private fun deleteLike (likeId: Long, music: Music) { + musicLikeRepository.deleteById(likeId) + music.minusLikeCount() + updateCache(music) + } + + private fun updateCache(music: Music) { + val cacheKey = music.createdDate.toLocalDate().toString() + redisCacheService.deleteFromCacheMusic(cacheKey) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/dotori/v2/global/config/redis/service/RedisCacheService.kt b/src/main/kotlin/com/dotori/v2/global/config/redis/service/RedisCacheService.kt index 8b408ce5..14672d4a 100644 --- a/src/main/kotlin/com/dotori/v2/global/config/redis/service/RedisCacheService.kt +++ b/src/main/kotlin/com/dotori/v2/global/config/redis/service/RedisCacheService.kt @@ -32,6 +32,10 @@ class RedisCacheService( return redisTemplate.opsForValue().get("musicList:$date") } + fun deleteFromCacheMusic(date: String) { + redisTemplate.delete("musicList:$date") + } + fun initCache(pattern: String) { val keys = redisTemplate.keys(pattern) if(keys.isNotEmpty()) { diff --git a/src/main/kotlin/com/dotori/v2/global/error/ErrorCode.kt b/src/main/kotlin/com/dotori/v2/global/error/ErrorCode.kt index b04a3fcd..f12cabde 100644 --- a/src/main/kotlin/com/dotori/v2/global/error/ErrorCode.kt +++ b/src/main/kotlin/com/dotori/v2/global/error/ErrorCode.kt @@ -67,6 +67,7 @@ enum class ErrorCode( MUSIC_TODAY_NOT_REQUESTED(HttpStatus.ACCEPTED.value(), "오늘 신청된 음악이 없습니다."), MUSIC_NOT_REQUEST_ON_THAT_DATE(HttpStatus.ACCEPTED.value(), "해당 날짜에 신청된 노래가 없습니다."), NOT_VALID_YOUTUBE_URL(HttpStatus.BAD_REQUEST.value(), "유효하지 않은 유튜브 url 입니다."), + NOT_VALID_MUSIC_LIKE(HttpStatus.BAD_REQUEST.value(), "이미 지난 요일의 기상음악에는 좋아요를 누를 수 없습니다."), // *** TOKEN ***