diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/tag/AdminMemberTagController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/tag/AdminMemberTagController.kt new file mode 100644 index 0000000..d78139e --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/tag/AdminMemberTagController.kt @@ -0,0 +1,42 @@ +package kr.co.vividnext.sodalive.admin.member.tag + +import kr.co.vividnext.sodalive.common.ApiResponse +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.DeleteMapping +import org.springframework.web.bind.annotation.PathVariable +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.RequestPart +import org.springframework.web.bind.annotation.RestController +import org.springframework.web.multipart.MultipartFile + +@RestController +@RequestMapping("/admin/member/tag") +class AdminMemberTagController(private val service: AdminMemberTagService) { + @PostMapping + @PreAuthorize("hasRole('ADMIN')") + fun enrollmentCreatorTag( + @RequestPart("image") image: MultipartFile, + @RequestPart("request") requestString: String + ) = ApiResponse.ok(service.uploadTagImage(image, requestString), "등록되었습니다.") + + @DeleteMapping("/{id}") + @PreAuthorize("hasRole('ADMIN')") + fun deleteCreatorTag(@PathVariable id: Long) = ApiResponse.ok(service.deleteTag(id), "삭제되었습니다.") + + @PutMapping("/{id}") + @PreAuthorize("hasRole('ADMIN')") + fun modifyCreatorTag( + @PathVariable id: Long, + @RequestPart("image") image: MultipartFile?, + @RequestPart("request") requestString: String + ) = ApiResponse.ok(service.modifyTag(id, image, requestString), "수정되었습니다.") + + @PutMapping("/orders") + @PreAuthorize("hasRole('ADMIN')") + fun updateTagOrders( + @RequestBody request: UpdateTagOrdersRequest + ) = ApiResponse.ok(service.updateTagOrders(request.ids), "수정되었습니다.") +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/tag/AdminMemberTagRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/tag/AdminMemberTagRepository.kt new file mode 100644 index 0000000..76061c7 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/tag/AdminMemberTagRepository.kt @@ -0,0 +1,22 @@ +package kr.co.vividnext.sodalive.admin.member.tag + +import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.member.tag.CreatorTag +import kr.co.vividnext.sodalive.member.tag.QCreatorTag.creatorTag +import org.springframework.data.jpa.repository.JpaRepository + +interface AdminMemberTagRepository : JpaRepository, AdminMemberTagQueryRepository + +interface AdminMemberTagQueryRepository { + fun findByTag(tag: String): Long? +} + +class AdminMemberTagQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : AdminMemberTagQueryRepository { + override fun findByTag(tag: String): Long? { + return queryFactory + .select(creatorTag.id) + .from(creatorTag) + .where(creatorTag.tag.eq(tag)) + .fetchFirst() + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/tag/AdminMemberTagService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/tag/AdminMemberTagService.kt new file mode 100644 index 0000000..0e60a59 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/tag/AdminMemberTagService.kt @@ -0,0 +1,83 @@ +package kr.co.vividnext.sodalive.admin.member.tag + +import com.fasterxml.jackson.databind.ObjectMapper +import kr.co.vividnext.sodalive.aws.s3.S3Uploader +import kr.co.vividnext.sodalive.common.SodaException +import kr.co.vividnext.sodalive.member.tag.CreatorTag +import kr.co.vividnext.sodalive.utils.generateFileName +import org.springframework.beans.factory.annotation.Value +import org.springframework.data.repository.findByIdOrNull +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import org.springframework.web.multipart.MultipartFile + +@Service +class AdminMemberTagService( + private val repository: AdminMemberTagRepository, + private val s3Uploader: S3Uploader, + private val objectMapper: ObjectMapper, + + @Value("\${cloud.aws.s3.bucket}") + private val bucket: String +) { + @Transactional + fun uploadTagImage(image: MultipartFile, requestString: String) { + val request = objectMapper.readValue(requestString, CreateMemberTagRequest::class.java) + tagExistCheck(request) + + val fileName = generateFileName() + val imagePath = s3Uploader.upload( + inputStream = image.inputStream, + bucket = bucket, + filePath = "creator_tag/$fileName" + ) + return createTag(request.tag, imagePath) + } + + private fun tagExistCheck(request: CreateMemberTagRequest) { + repository.findByTag(request.tag)?.let { throw SodaException("이미 등록된 태그 입니다.") } + } + + private fun createTag(tag: String, imagePath: String) { + repository.save(CreatorTag(tag, imagePath)) + } + + @Transactional + fun deleteTag(id: Long) { + val creatorTag = repository.findByIdOrNull(id) + ?: throw SodaException("잘못된 요청입니다.") + + creatorTag.tag = "${creatorTag.tag}_deleted" + creatorTag.isActive = false + } + + @Transactional + fun modifyTag(id: Long, image: MultipartFile?, requestString: String) { + val creatorTag = repository.findByIdOrNull(id) + ?: throw SodaException("잘못된 요청입니다.") + + val request = objectMapper.readValue(requestString, CreateMemberTagRequest::class.java) + creatorTag.tag = request.tag + + if (image != null) { + val fileName = generateFileName() + val imagePath = s3Uploader.upload( + inputStream = image.inputStream, + bucket = bucket, + filePath = "creator_tag/$fileName" + ) + creatorTag.image = imagePath + } + } + + @Transactional + fun updateTagOrders(ids: List) { + for (index in ids.indices) { + val tag = repository.findByIdOrNull(ids[index]) + + if (tag != null) { + tag.orders = index + 1 + } + } + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/tag/CreateMemberTagRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/tag/CreateMemberTagRequest.kt new file mode 100644 index 0000000..fa70355 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/tag/CreateMemberTagRequest.kt @@ -0,0 +1,3 @@ +package kr.co.vividnext.sodalive.admin.member.tag + +data class CreateMemberTagRequest(val tag: String) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/tag/UpdateTagOrdersRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/tag/UpdateTagOrdersRequest.kt new file mode 100644 index 0000000..4da263a --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/member/tag/UpdateTagOrdersRequest.kt @@ -0,0 +1,3 @@ +package kr.co.vividnext.sodalive.admin.member.tag + +data class UpdateTagOrdersRequest(val ids: List) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/member/tag/GetMemberTagResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/member/tag/GetMemberTagResponse.kt new file mode 100644 index 0000000..a98882b --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/member/tag/GetMemberTagResponse.kt @@ -0,0 +1,9 @@ +package kr.co.vividnext.sodalive.member.tag + +import com.querydsl.core.annotations.QueryProjection + +data class GetMemberTagResponse @QueryProjection constructor( + val id: Long, + val tag: String, + val image: String +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/member/tag/MemberTagController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/member/tag/MemberTagController.kt new file mode 100644 index 0000000..ff4c7f5 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/member/tag/MemberTagController.kt @@ -0,0 +1,13 @@ +package kr.co.vividnext.sodalive.member.tag + +import kr.co.vividnext.sodalive.common.ApiResponse +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping("/member/tag") +class MemberTagController(private val service: MemberTagService) { + @GetMapping + fun getTags() = ApiResponse.ok(service.getTags()) +} 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 new file mode 100644 index 0000000..84a2e62 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/member/tag/MemberTagRepository.kt @@ -0,0 +1,34 @@ +package kr.co.vividnext.sodalive.member.tag + +import com.querydsl.jpa.impl.JPAQueryFactory +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 MemberTagQueryRepository { + fun getTags(): List +} + +class MemberTagQueryRepositoryImpl( + private val queryFactory: JPAQueryFactory, + + @Value("\${cloud.aws.cloud-front.host}") + private val cloudFrontHost: String +) : MemberTagQueryRepository { + override fun getTags(): List { + return queryFactory + .select( + QGetMemberTagResponse( + creatorTag.id, + creatorTag.tag, + creatorTag.image.prepend("/").prepend(cloudFrontHost) + ) + ) + .from(creatorTag) + .where(creatorTag.isActive.eq(true)) + .orderBy(creatorTag.orders.asc()) + .fetch() + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/member/tag/MemberTagService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/member/tag/MemberTagService.kt new file mode 100644 index 0000000..72a51b3 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/member/tag/MemberTagService.kt @@ -0,0 +1,10 @@ +package kr.co.vividnext.sodalive.member.tag + +import org.springframework.stereotype.Service + +@Service +class MemberTagService(private val repository: MemberTagRepository) { + fun getTags(): List { + return repository.getTags() + } +}