feat(banner): 정렬 순서 추가

This commit is contained in:
2025-08-07 15:31:03 +09:00
parent 81f972edc1
commit ef8458c7a3
5 changed files with 80 additions and 8 deletions

View File

@@ -9,6 +9,7 @@ import javax.persistence.ManyToOne
/**
* 캐릭터 배너 엔티티
* 이미지와 캐릭터 ID를 가지며, 소프트 삭제(isActive = false)를 지원합니다.
* 정렬 순서(sortOrder)를 통해 배너의 표시 순서를 결정합니다.
*/
@Entity
class ChatCharacterBanner(
@@ -20,6 +21,9 @@ class ChatCharacterBanner(
@JoinColumn(name = "character_id")
var chatCharacter: ChatCharacter,
// 정렬 순서 (낮을수록 먼저 표시)
var sortOrder: Int = 0,
// 활성화 여부 (소프트 삭제용)
var isActive: Boolean = true
) : BaseEntity()

View File

@@ -4,10 +4,15 @@ import kr.co.vividnext.sodalive.chat.character.ChatCharacterBanner
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import org.springframework.stereotype.Repository
@Repository
interface ChatCharacterBannerRepository : JpaRepository<ChatCharacterBanner, Long> {
// 활성화된 배너 목록 조회
fun findByActiveTrue(pageable: Pageable): Page<ChatCharacterBanner>
// 활성화된 배너 목록 조회 (정렬 순서대로)
fun findByActiveTrueOrderBySortOrderAsc(pageable: Pageable): Page<ChatCharacterBanner>
// 활성화된 배너 중 최대 정렬 순서 값 조회
@Query("SELECT MAX(b.sortOrder) FROM ChatCharacterBanner b WHERE b.isActive = true")
fun findMaxSortOrder(): Int?
}

View File

@@ -15,10 +15,10 @@ class ChatCharacterBannerService(
private val characterRepository: ChatCharacterRepository
) {
/**
* 활성화된 모든 배너 조회
* 활성화된 모든 배너 조회 (정렬 순서대로)
*/
fun getActiveBanners(pageable: Pageable): Page<ChatCharacterBanner> {
return bannerRepository.findByActiveTrue(pageable)
return bannerRepository.findByActiveTrueOrderBySortOrderAsc(pageable)
}
/**
@@ -31,6 +31,10 @@ class ChatCharacterBannerService(
/**
* 배너 등록
*
* @param characterId 캐릭터 ID
* @param imagePath 이미지 경로
* @return 등록된 배너
*/
@Transactional
fun registerBanner(characterId: Long, imagePath: String): ChatCharacterBanner {
@@ -41,9 +45,13 @@ class ChatCharacterBannerService(
throw SodaException("비활성화된 캐릭터에는 배너를 등록할 수 없습니다: $characterId")
}
// 정렬 순서가 지정되지 않은 경우 가장 마지막 순서로 설정
val finalSortOrder = (bannerRepository.findMaxSortOrder() ?: 0) + 1
val banner = ChatCharacterBanner(
imagePath = imagePath,
chatCharacter = character
chatCharacter = character,
sortOrder = finalSortOrder
)
return bannerRepository.save(banner)
@@ -97,4 +105,30 @@ class ChatCharacterBannerService(
banner.isActive = false
bannerRepository.save(banner)
}
/**
* 배너 정렬 순서 일괄 변경
* ID 목록의 순서대로 정렬 순서를 1부터 순차적으로 설정합니다.
*
* @param ids 배너 ID 목록 (순서대로 정렬됨)
* @return 수정된 배너 목록
*/
@Transactional
fun updateBannerOrders(ids: List<Long>): List<ChatCharacterBanner> {
val updatedBanners = mutableListOf<ChatCharacterBanner>()
for (index in ids.indices) {
val banner = bannerRepository.findById(ids[index])
.orElseThrow { SodaException("배너를 찾을 수 없습니다: ${ids[index]}") }
if (!banner.isActive) {
throw SodaException("비활성화된 배너는 수정할 수 없습니다: ${ids[index]}")
}
banner.sortOrder = index + 1
updatedBanners.add(bannerRepository.save(banner))
}
return updatedBanners
}
}