diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/explorer/AdminExplorerController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/explorer/AdminExplorerController.kt new file mode 100644 index 0000000..59db936 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/explorer/AdminExplorerController.kt @@ -0,0 +1,39 @@ +package kr.co.vividnext.sodalive.admin.explorer + +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.PostMapping +import org.springframework.web.bind.annotation.PutMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping("/admin/explorer") +@PreAuthorize("hasRole('ADMIN')") +class AdminExplorerController(private val service: AdminExplorerService) { + @PostMapping + fun createExplorerSection(@RequestBody request: CreateExplorerSectionRequest): ApiResponse { + service.createExplorerSection(request) + return ApiResponse.ok(null, "등록되었습니다.") + } + + @PutMapping + fun updateExplorerSection(@RequestBody request: UpdateExplorerSectionRequest) = ApiResponse.ok( + service.updateExplorerSection(request), + "수정되었습니다." + ) + + @PutMapping("/orders") + fun updateExplorerSectionOrders( + @RequestBody request: UpdateExplorerSectionOrdersRequest + ) = ApiResponse.ok( + service.updateExplorerSectionOrders(request.firstOrders, request.ids), + "수정되었습니다." + ) + + @GetMapping + fun getExplorerSections(pageable: Pageable) = ApiResponse.ok(service.getExplorerSections(pageable)) +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/explorer/AdminExplorerSectionRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/explorer/AdminExplorerSectionRepository.kt new file mode 100644 index 0000000..347da74 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/explorer/AdminExplorerSectionRepository.kt @@ -0,0 +1,43 @@ +package kr.co.vividnext.sodalive.admin.explorer + +import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.explorer.section.ExplorerSection +import kr.co.vividnext.sodalive.explorer.section.QExplorerSection.explorerSection +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository + +@Repository +interface AdminExplorerSectionRepository : JpaRepository, AdminExplorerSectionQueryRepository + +interface AdminExplorerSectionQueryRepository { + fun findByTitle(title: String): ExplorerSection? + fun findAllWithPaging(offset: Long, limit: Long): List + fun totalCount(): Int +} + +class AdminExplorerSectionQueryRepositoryImpl( + private val queryFactory: JPAQueryFactory +) : AdminExplorerSectionQueryRepository { + override fun findByTitle(title: String): ExplorerSection? { + return queryFactory + .selectFrom(explorerSection) + .where(explorerSection.title.eq(title)) + .fetchFirst() + } + + override fun findAllWithPaging(offset: Long, limit: Long): List { + return queryFactory + .selectFrom(explorerSection) + .offset(offset) + .limit(limit) + .orderBy(explorerSection.orders.asc()) + .fetch() + } + + override fun totalCount(): Int { + return queryFactory + .selectFrom(explorerSection) + .fetch() + .size + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/explorer/AdminExplorerService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/explorer/AdminExplorerService.kt new file mode 100644 index 0000000..7bab12d --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/explorer/AdminExplorerService.kt @@ -0,0 +1,144 @@ +package kr.co.vividnext.sodalive.admin.explorer + +import kr.co.vividnext.sodalive.common.SodaException +import kr.co.vividnext.sodalive.explorer.section.ExplorerSection +import kr.co.vividnext.sodalive.explorer.section.ExplorerSectionCreatorTag +import kr.co.vividnext.sodalive.member.tag.MemberTagRepository +import org.springframework.data.domain.Pageable +import org.springframework.data.repository.findByIdOrNull +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +@Transactional(readOnly = true) +class AdminExplorerService( + private val repository: AdminExplorerSectionRepository, + private val memberTagRepository: MemberTagRepository +) { + @Transactional + fun createExplorerSection(request: CreateExplorerSectionRequest): Long { + if (request.title.isBlank()) throw SodaException("제목을 입력하세요.") + + val findExplorerSection = repository.findByTitle(request.title) + if (findExplorerSection != null) throw SodaException("동일한 제목이 있습니다.") + + val explorerSection = ExplorerSection(title = request.title, isAdult = request.isAdult) + explorerSection.coloredTitle = request.coloredTitle + explorerSection.color = request.color + + val tags = mutableListOf() + request.tagList.forEach { + val findTag = memberTagRepository.findByTag(it) + if (findTag != null) { + val tag = ExplorerSectionCreatorTag() + tag.explorerSection = explorerSection + tag.tag = findTag + tags.add(tag) + } + } + + if (tags.size <= 0) throw SodaException("관심사를 선택하세요.") + explorerSection.tags = tags + + return repository.save(explorerSection).id!! + } + + @Transactional + fun updateExplorerSection(request: UpdateExplorerSectionRequest) { + if ( + request.title == null && + request.isAdult == null && + request.tagList == null && + request.color == null && + request.coloredTitle == null && + request.isActive == null + ) { + throw SodaException("변경사항이 없습니다.") + } + + val explorerSection = repository.findByIdOrNull(request.id) + ?: throw SodaException("해당하는 섹션이 없습니다.") + + if (request.title != null) { + if (request.title.isBlank()) throw SodaException("올바른 제목을 입력하세요.") + explorerSection.title = request.title + } + + if (request.isActive != null) { + explorerSection.isActive = request.isActive + } + + if (request.isAdult != null) { + explorerSection.isAdult = request.isAdult + } + + if (request.color != null) { + explorerSection.color = request.color + } + + if (request.coloredTitle != null) { + explorerSection.coloredTitle = request.coloredTitle + } + + if (request.tagList != null) { + val requestTagList = request.tagList.toMutableList() + val tags = explorerSection.tags.filter { + requestTagList.contains(it.tag!!.tag) + requestTagList.remove(it.tag!!.tag) + }.toMutableList() + + requestTagList.forEach { + val findTag = memberTagRepository.findByTag(it) + if (findTag != null) { + val tag = ExplorerSectionCreatorTag() + tag.explorerSection = explorerSection + tag.tag = findTag + tags.add(tag) + } + } + + if (tags.size <= 0) throw SodaException("관심사를 입력하세요.") + if (tags != explorerSection.tags) { + explorerSection.tags.clear() + explorerSection.tags.addAll(tags) + } + } + } + + @Transactional + fun updateExplorerSectionOrders(firstOrders: Int, ids: List) { + for (index in ids.indices) { + val explorerSection = repository.findByIdOrNull(ids[index]) + + if (explorerSection != null) { + explorerSection.orders = firstOrders + index + } + } + } + + fun getExplorerSections(pageable: Pageable): GetAdminExplorerSectionResponse { + val totalCount = repository.totalCount() + val explorerSectionItemList = repository + .findAllWithPaging(pageable.offset, pageable.pageSize.toLong()) + .map { + GetAdminExplorerSectionResponseItem( + id = it.id!!, + title = it.title, + coloredTitle = it.coloredTitle ?: "", + color = it.color ?: "", + isAdult = it.isAdult, + isActive = it.isActive, + tags = it.tags + .asSequence() + .filter { explorerSectionTag -> explorerSectionTag.tag!!.isActive } + .map { explorerSectionTag -> explorerSectionTag.tag!!.tag } + .toList() + ) + } + + return GetAdminExplorerSectionResponse( + totalCount = totalCount, + explorerSectionItemList = explorerSectionItemList + ) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/explorer/CreateExplorerSectionRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/explorer/CreateExplorerSectionRequest.kt new file mode 100644 index 0000000..7e43118 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/explorer/CreateExplorerSectionRequest.kt @@ -0,0 +1,9 @@ +package kr.co.vividnext.sodalive.admin.explorer + +data class CreateExplorerSectionRequest( + val title: String, + val isAdult: Boolean, + val tagList: List, + val coloredTitle: String? = null, + val color: String? = null +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/explorer/GetAdminExplorerSectionResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/explorer/GetAdminExplorerSectionResponse.kt new file mode 100644 index 0000000..049be02 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/explorer/GetAdminExplorerSectionResponse.kt @@ -0,0 +1,16 @@ +package kr.co.vividnext.sodalive.admin.explorer + +data class GetAdminExplorerSectionResponse( + val totalCount: Int, + val explorerSectionItemList: List +) + +data class GetAdminExplorerSectionResponseItem( + val id: Long, + val title: String, + val coloredTitle: String, + val color: String, + val isAdult: Boolean, + val isActive: Boolean, + val tags: List +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/explorer/UpdateExplorerSectionOrdersRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/explorer/UpdateExplorerSectionOrdersRequest.kt new file mode 100644 index 0000000..ff3f2c2 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/explorer/UpdateExplorerSectionOrdersRequest.kt @@ -0,0 +1,6 @@ +package kr.co.vividnext.sodalive.admin.explorer + +data class UpdateExplorerSectionOrdersRequest( + val firstOrders: Int, + val ids: List +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/explorer/UpdateExplorerSectionRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/explorer/UpdateExplorerSectionRequest.kt new file mode 100644 index 0000000..ee3d38f --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/explorer/UpdateExplorerSectionRequest.kt @@ -0,0 +1,11 @@ +package kr.co.vividnext.sodalive.admin.explorer + +data class UpdateExplorerSectionRequest( + val id: Long, + val title: String? = null, + val isAdult: Boolean? = null, + val tagList: List? = null, + val coloredTitle: String? = null, + val color: String? = null, + val isActive: Boolean? = null +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/member/tag/MemberTagRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/member/tag/MemberTagRepository.kt index 84a2e62..078a252 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/member/tag/MemberTagRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/member/tag/MemberTagRepository.kt @@ -5,7 +5,9 @@ import kr.co.vividnext.sodalive.member.tag.QCreatorTag.creatorTag import org.springframework.beans.factory.annotation.Value import org.springframework.data.jpa.repository.JpaRepository -interface MemberTagRepository : JpaRepository, MemberTagQueryRepository +interface MemberTagRepository : JpaRepository, MemberTagQueryRepository { + fun findByTag(tag: String): CreatorTag? +} interface MemberTagQueryRepository { fun getTags(): List