diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanChargeRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanChargeRequest.kt index bbb8fd0..22cfb98 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanChargeRequest.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanChargeRequest.kt @@ -1,7 +1,7 @@ package kr.co.vividnext.sodalive.admin.can data class AdminCanChargeRequest( - val memberId: Long, + val memberIds: List, val method: String, val can: Int ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanService.kt index 612e414..9e780dc 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanService.kt @@ -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 + } } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/AdminMemberController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/AdminMemberController.kt index 7070169..ca129a2 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/AdminMemberController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/AdminMemberController.kt @@ -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()) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/AdminMemberRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/AdminMemberRepository.kt index 538259b..4249bd3 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/AdminMemberRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/AdminMemberRepository.kt @@ -16,6 +16,7 @@ interface AdminMemberQueryRepository { fun searchMemberTotalCount(searchWord: String, role: MemberRole? = null): Int fun getCreatorAllList(): List fun findByIdAndActive(memberId: Long): Member? + fun searchMemberByNickname(searchWord: String, limit: Long = 20): List } 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 { + 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() + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/AdminMemberService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/AdminMemberService.kt index 3946cbe..5a1f50f 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/AdminMemberService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/AdminMemberService.kt @@ -145,6 +145,12 @@ class AdminMemberService( return repository.getCreatorAllList() } + fun searchMemberByNickname(searchWord: String, size: Int = 20): List { + 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) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/AdminSimpleMemberResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/AdminSimpleMemberResponse.kt new file mode 100644 index 0000000..00d0ea5 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/AdminSimpleMemberResponse.kt @@ -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 +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/api/home/GetHomeResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/api/home/GetHomeResponse.kt index aa11948..efbd497 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/api/home/GetHomeResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/api/home/GetHomeResponse.kt @@ -26,5 +26,6 @@ data class GetHomeResponse( val contentRanking: List, val recommendChannelList: List, val freeContentList: List, + val pointAvailableContentList: List, val curationList: List ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeService.kt index 5d66fe5..b45db6e 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeService.kt @@ -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 ) } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt index 66abb79..6b93c4d 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt @@ -183,7 +183,9 @@ interface AudioContentQueryRepository { offset: Long, limit: Long, isFree: Boolean, - isAdult: Boolean + isAdult: Boolean, + orderByRandom: Boolean = false, + isPointAvailableOnly: Boolean = false ): List fun findContentByCurationId( @@ -1308,7 +1310,9 @@ class AudioContentQueryRepositoryImpl( offset: Long, limit: Long, isFree: Boolean, - isAdult: Boolean + isAdult: Boolean, + orderByRandom: Boolean, + isPointAvailableOnly: Boolean ): List { 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() } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt index 3a9e3a3..23bca75 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt @@ -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 { 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 ) } }