From cbcc63dc71bc88045a1045062176051caec9109a Mon Sep 17 00:00:00 2001 From: Klaus <klaus@vividnext.co.kr> Date: Sun, 6 Aug 2023 11:13:27 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B4=80=EB=A6=AC=EC=9E=90=20-=20=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=EB=A6=AC=EC=8A=A4=ED=8A=B8=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/member/AdminMemberController.kt | 23 +++++ .../admin/member/AdminMemberRepository.kt | 95 +++++++++++++++++++ .../admin/member/AdminMemberService.kt | 92 ++++++++++++++++++ .../member/GetAdminMemberListResponse.kt | 19 ++++ 4 files changed, 229 insertions(+) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/member/AdminMemberController.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/member/AdminMemberRepository.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/member/AdminMemberService.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/member/GetAdminMemberListResponse.kt 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 new file mode 100644 index 0000000..78389da --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/AdminMemberController.kt @@ -0,0 +1,23 @@ +package kr.co.vividnext.sodalive.admin.member + +import kr.co.vividnext.sodalive.common.ApiResponse +import org.springframework.data.domain.Pageable +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping("/admin/member") +class AdminMemberController(private val service: AdminMemberService) { + @GetMapping("/list") + @PreAuthorize("hasRole('ADMIN')") + fun getMemberList(pageable: Pageable) = ApiResponse.ok(service.getMemberList(pageable)) + + @GetMapping("/search") + fun searchMember( + @RequestParam(value = "search_word") searchWord: String, + pageable: Pageable + ) = ApiResponse.ok(service.searchMember(searchWord, pageable)) +} 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 new file mode 100644 index 0000000..9d20fe9 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/AdminMemberRepository.kt @@ -0,0 +1,95 @@ +package kr.co.vividnext.sodalive.admin.member + +import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.member.Member +import kr.co.vividnext.sodalive.member.MemberRole +import kr.co.vividnext.sodalive.member.QMember.member +import org.springframework.data.jpa.repository.JpaRepository + +interface AdminMemberRepository : JpaRepository<Member, Long>, AdminMemberQueryRepository + +interface AdminMemberQueryRepository { + fun getMemberTotalCount(role: MemberRole? = null): Int + fun getMemberList(offset: Long, limit: Long, role: MemberRole? = null): List<Member> + fun searchMember(searchWord: String, offset: Long, limit: Long, role: MemberRole? = null): List<Member> + + fun searchMemberTotalCount(searchWord: String, role: MemberRole? = null): Int +} + +class AdminMemberQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : AdminMemberQueryRepository { + override fun getMemberList(offset: Long, limit: Long, role: MemberRole?): List<Member> { + return queryFactory + .selectFrom(member) + .where( + member.role.ne(MemberRole.ADMIN) + .and( + if (role != null) { + member.role.eq(role) + } else { + null + } + ) + ) + .offset(offset) + .limit(limit) + .orderBy(member.id.desc()) + .fetch() + } + + override fun getMemberTotalCount(role: MemberRole?): Int { + return queryFactory + .select(member.id) + .from(member) + .where( + member.id.gt(1) + .and( + if (role != null) { + member.role.eq(role) + } else { + null + } + ) + ) + .fetch() + .size + } + + override fun searchMember(searchWord: String, offset: Long, limit: Long, role: MemberRole?): List<Member> { + return queryFactory + .selectFrom(member) + .where( + member.nickname.contains(searchWord) + .or(member.email.contains(searchWord)) + .and( + if (role != null) { + member.role.eq(role) + } else { + null + } + ) + ) + .offset(offset) + .limit(limit) + .orderBy(member.id.desc()) + .fetch() + } + + override fun searchMemberTotalCount(searchWord: String, role: MemberRole?): Int { + return queryFactory + .select(member.id) + .from(member) + .where( + member.nickname.contains(searchWord) + .or(member.email.contains(searchWord)) + .and( + if (role != null) { + member.role.eq(role) + } else { + null + } + ) + ) + .fetch() + .size + } +} 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 new file mode 100644 index 0000000..49f1d46 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/AdminMemberService.kt @@ -0,0 +1,92 @@ +package kr.co.vividnext.sodalive.admin.member + +import kr.co.vividnext.sodalive.common.SodaException +import kr.co.vividnext.sodalive.member.Member +import kr.co.vividnext.sodalive.member.MemberRole +import org.springframework.beans.factory.annotation.Value +import org.springframework.data.domain.Pageable +import org.springframework.stereotype.Service +import java.time.ZoneId +import java.time.format.DateTimeFormatter + +@Service +class AdminMemberService( + private val repository: AdminMemberRepository, + + @Value("\${cloud.aws.cloud-front.host}") + private val cloudFrontHost: String +) { + fun getMemberList(pageable: Pageable): GetAdminMemberListResponse { + val totalCount = repository.getMemberTotalCount() + val memberList = processMemberListToGetAdminMemberListResponseItemList( + memberList = repository.getMemberList( + offset = pageable.offset, + limit = pageable.pageSize.toLong() + ) + ) + + return GetAdminMemberListResponse(totalCount, memberList) + } + + fun searchMember(searchWord: String, pageable: Pageable): GetAdminMemberListResponse { + if (searchWord.length < 2) throw SodaException("2글자 이상 입력하세요.") + val totalCount = repository.searchMemberTotalCount(searchWord = searchWord) + val memberList = processMemberListToGetAdminMemberListResponseItemList( + memberList = repository.searchMember( + searchWord = searchWord, + offset = pageable.offset, + limit = pageable.pageSize.toLong() + ) + ) + + return GetAdminMemberListResponse(totalCount, memberList) + } + + private fun processMemberListToGetAdminMemberListResponseItemList( + memberList: List<Member> + ): List<GetAdminMemberListResponseItem> { + return memberList + .asSequence() + .map { + val userType = when (it.role) { + MemberRole.ADMIN -> "관리자" + MemberRole.USER -> "일반회원" + MemberRole.CREATOR -> "요즘친구" + MemberRole.AGENT -> "에이전트" + MemberRole.BOT -> "봇" + } + + val signUpDate = it.createdAt!! + .atZone(ZoneId.of("UTC")) + .withZoneSameInstant(ZoneId.of("Asia/Seoul")) + .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) + + val signOutDate = if (it.signOutReasons.isNotEmpty()) { + it.signOutReasons.last().createdAt!! + .atZone(ZoneId.of("UTC")) + .withZoneSameInstant(ZoneId.of("Asia/Seoul")) + .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) + } else { + "" + } + + GetAdminMemberListResponseItem( + id = it.id!!, + email = it.email, + nickname = it.nickname, + profileUrl = if (it.profileImage != null) { + "$cloudFrontHost/${it.profileImage}" + } else { + "$cloudFrontHost/profile/default-profile.png" + }, + userType = userType, + container = it.container, + auth = it.auth != null, + signUpDate = signUpDate, + signOutDate = signOutDate, + isActive = it.isActive + ) + } + .toList() + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/GetAdminMemberListResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/GetAdminMemberListResponse.kt new file mode 100644 index 0000000..1fd1c47 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/GetAdminMemberListResponse.kt @@ -0,0 +1,19 @@ +package kr.co.vividnext.sodalive.admin.member + +data class GetAdminMemberListResponse( + val totalCount: Int, + val items: List<GetAdminMemberListResponseItem> +) + +data class GetAdminMemberListResponseItem( + val id: Long, + val email: String, + val nickname: String, + val profileUrl: String, + val userType: String, + val container: String, + val auth: Boolean, + val signUpDate: String, + val signOutDate: String, + val isActive: Boolean +)