Merge pull request 'test' (#358) from test into main

Reviewed-on: #358
This commit is contained in:
2025-11-10 06:53:41 +00:00
10 changed files with 103 additions and 18 deletions

View File

@@ -1,7 +1,7 @@
package kr.co.vividnext.sodalive.admin.can
data class AdminCanChargeRequest(
val memberId: Long,
val memberIds: List<Long>,
val method: String,
val can: Int
)

View File

@@ -40,22 +40,27 @@ class AdminCanService(
@Transactional
fun charge(request: AdminCanChargeRequest) {
val member = memberRepository.findByIdOrNull(request.memberId)
?: throw SodaException("잘못된 회원번호 입니다.")
if (request.can <= 0) throw SodaException("1 캔 이상 입력하세요.")
if (request.method.isBlank()) throw SodaException("기록내용을 입력하세요.")
val charge = Charge(0, request.can, status = ChargeStatus.ADMIN)
charge.title = "${request.can.moneyFormat()}"
charge.member = member
val ids = request.memberIds.distinct()
if (ids.isEmpty()) throw SodaException("회원번호를 입력하세요.")
val payment = Payment(status = PaymentStatus.COMPLETE, paymentGateway = PaymentGateway.PG)
payment.method = request.method
charge.payment = payment
val members = memberRepository.findAllById(ids).toList()
if (members.size != ids.size) throw SodaException("잘못된 회원번호 입니다.")
chargeRepository.save(charge)
members.forEach { member ->
val charge = Charge(0, request.can, status = ChargeStatus.ADMIN)
charge.title = "${request.can.moneyFormat()}"
charge.member = member
member.pgRewardCan += charge.rewardCan
val payment = Payment(status = PaymentStatus.COMPLETE, paymentGateway = PaymentGateway.PG)
payment.method = request.method
charge.payment = payment
chargeRepository.save(charge)
member.pgRewardCan += charge.rewardCan
}
}
}

View File

@@ -36,6 +36,12 @@ class AdminMemberController(private val service: AdminMemberService) {
pageable: Pageable
) = ApiResponse.ok(service.searchMember(searchWord, pageable))
@GetMapping("/search-by-nickname")
fun searchMemberByNickname(
@RequestParam(value = "search_word") searchWord: String,
@RequestParam(value = "size", required = false) size: Int?
) = ApiResponse.ok(service.searchMemberByNickname(searchWord = searchWord, size = size ?: 20))
@GetMapping("/creator/all/list")
fun getCreatorAllList() = ApiResponse.ok(service.getCreatorAllList())

View File

@@ -16,6 +16,7 @@ interface AdminMemberQueryRepository {
fun searchMemberTotalCount(searchWord: String, role: MemberRole? = null): Int
fun getCreatorAllList(): List<GetAdminCreatorAllListResponse>
fun findByIdAndActive(memberId: Long): Member?
fun searchMemberByNickname(searchWord: String, limit: Long = 20): List<AdminSimpleMemberResponse>
}
class AdminMemberQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : AdminMemberQueryRepository {
@@ -121,4 +122,22 @@ class AdminMemberQueryRepositoryImpl(private val queryFactory: JPAQueryFactory)
.orderBy(member.id.desc())
.fetchFirst()
}
override fun searchMemberByNickname(searchWord: String, limit: Long): List<AdminSimpleMemberResponse> {
return queryFactory
.select(
QAdminSimpleMemberResponse(
member.id,
member.nickname
)
)
.from(member)
.where(
member.nickname.contains(searchWord)
.and(member.isActive.isTrue)
)
.orderBy(member.id.desc())
.limit(limit)
.fetch()
}
}

View File

@@ -145,6 +145,12 @@ class AdminMemberService(
return repository.getCreatorAllList()
}
fun searchMemberByNickname(searchWord: String, size: Int = 20): List<AdminSimpleMemberResponse> {
if (searchWord.length < 2) throw SodaException("2글자 이상 입력하세요.")
val limit = if (size <= 0) 20 else size
return repository.searchMemberByNickname(searchWord = searchWord, limit = limit.toLong())
}
@Transactional
fun resetPassword(request: ResetPasswordRequest) {
val member = repository.findByIdAndActive(memberId = request.memberId)

View File

@@ -0,0 +1,12 @@
package kr.co.vividnext.sodalive.admin.member
import com.querydsl.core.annotations.QueryProjection
/**
* 관리자용 간단 회원 응답 DTO
* 닉네임 검색 결과로 사용되며 charge 등에서 memberId 선택에 활용된다.
*/
data class AdminSimpleMemberResponse @QueryProjection constructor(
val id: Long,
val nickname: String
)

View File

@@ -26,5 +26,6 @@ data class GetHomeResponse(
val contentRanking: List<GetAudioContentRankingItem>,
val recommendChannelList: List<RecommendChannelResponse>,
val freeContentList: List<AudioContentMainItem>,
val pointAvailableContentList: List<AudioContentMainItem>,
val curationList: List<GetContentCurationResponse>
)

View File

@@ -165,7 +165,24 @@ class HomeService(
),
contentType = contentType,
isFree = true,
isAdult = isAdult
isAdult = isAdult,
orderByRandom = true
).filter {
if (memberId != null) {
!memberService.isBlocked(blockedMemberId = memberId, memberId = it.creatorId)
} else {
true
}
}
// 포인트 사용가능 콘텐츠 리스트 - 랜덤으로 가져오기 (DB에서 isPointAvailable 조건 적용)
val pointAvailableContentList = contentService.getLatestContentByTheme(
theme = emptyList(),
contentType = contentType,
isFree = false,
isAdult = isAdult,
orderByRandom = true,
isPointAvailableOnly = true
).filter {
if (memberId != null) {
!memberService.isBlocked(blockedMemberId = memberId, memberId = it.creatorId)
@@ -195,6 +212,7 @@ class HomeService(
contentRanking = contentRanking,
recommendChannelList = recommendChannelList,
freeContentList = freeContentList,
pointAvailableContentList = pointAvailableContentList,
curationList = curationList
)
}

View File

@@ -183,7 +183,9 @@ interface AudioContentQueryRepository {
offset: Long,
limit: Long,
isFree: Boolean,
isAdult: Boolean
isAdult: Boolean,
orderByRandom: Boolean = false,
isPointAvailableOnly: Boolean = false
): List<AudioContentMainItem>
fun findContentByCurationId(
@@ -1308,7 +1310,9 @@ class AudioContentQueryRepositoryImpl(
offset: Long,
limit: Long,
isFree: Boolean,
isAdult: Boolean
isAdult: Boolean,
orderByRandom: Boolean,
isPointAvailableOnly: Boolean
): List<AudioContentMainItem> {
var where = audioContent.isActive.isTrue
.and(audioContent.duration.isNotNull)
@@ -1343,6 +1347,16 @@ class AudioContentQueryRepositoryImpl(
where = where.and(audioContent.price.loe(0))
}
if (isPointAvailableOnly) {
where = where.and(audioContent.isPointAvailable.isTrue)
}
val orderBy = if (orderByRandom) {
Expressions.numberTemplate(Double::class.java, "function('rand')").asc()
} else {
audioContent.releaseDate.desc()
}
return queryFactory
.select(
QAudioContentMainItem(
@@ -1360,7 +1374,7 @@ class AudioContentQueryRepositoryImpl(
.where(where)
.offset(offset)
.limit(limit)
.orderBy(audioContent.releaseDate.desc())
.orderBy(orderBy)
.fetch()
}

View File

@@ -989,7 +989,9 @@ class AudioContentService(
offset: Long = 0,
limit: Long = 20,
isFree: Boolean = false,
isAdult: Boolean = false
isAdult: Boolean = false,
orderByRandom: Boolean = false,
isPointAvailableOnly: Boolean = false
): List<AudioContentMainItem> {
return repository.getLatestContentByTheme(
theme = theme,
@@ -997,7 +999,9 @@ class AudioContentService(
offset = offset,
limit = limit,
isFree = isFree,
isAdult = isAdult
isAdult = isAdult,
orderByRandom = orderByRandom,
isPointAvailableOnly = isPointAvailableOnly
)
}
}