From 7b85558ffb6d4739c2e555ea81d799f61121d326 Mon Sep 17 00:00:00 2001 From: dldmsql Date: Wed, 4 Dec 2024 23:07:21 +0900 Subject: [PATCH] =?UTF-8?q?feat:=206=EC=B0=A8=20mvp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sticker/controller/StickerController.kt | 45 +++++ .../domain/sticker/dto/StickerUseRequest.kt | 13 ++ .../repository/StickerJooqRepository.kt | 9 + .../repository/StickerJooqRepositoryImpl.kt | 39 ++++ .../domain/sticker/service/StickerService.kt | 22 ++ .../SonnyPolaboBeApplicationTests.kt | 190 ------------------ 6 files changed, 128 insertions(+), 190 deletions(-) create mode 100644 src/main/kotlin/com/ddd/sonnypolabobe/domain/sticker/controller/StickerController.kt create mode 100644 src/main/kotlin/com/ddd/sonnypolabobe/domain/sticker/dto/StickerUseRequest.kt create mode 100644 src/main/kotlin/com/ddd/sonnypolabobe/domain/sticker/repository/StickerJooqRepository.kt create mode 100644 src/main/kotlin/com/ddd/sonnypolabobe/domain/sticker/repository/StickerJooqRepositoryImpl.kt create mode 100644 src/main/kotlin/com/ddd/sonnypolabobe/domain/sticker/service/StickerService.kt diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/domain/sticker/controller/StickerController.kt b/src/main/kotlin/com/ddd/sonnypolabobe/domain/sticker/controller/StickerController.kt new file mode 100644 index 0000000..a33923d --- /dev/null +++ b/src/main/kotlin/com/ddd/sonnypolabobe/domain/sticker/controller/StickerController.kt @@ -0,0 +1,45 @@ +package com.ddd.sonnypolabobe.domain.sticker.controller + +import com.ddd.sonnypolabobe.domain.sticker.dto.StickerUseRequest +import com.ddd.sonnypolabobe.domain.sticker.service.StickerService +import com.ddd.sonnypolabobe.domain.user.dto.UserDto +import com.ddd.sonnypolabobe.global.response.ApplicationResponse +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.tags.Tag +import jakarta.validation.Valid +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping("/api/v1/stickers") +class StickerController( + private val stickerService: StickerService +) { + + @Tag(name = "1.5.0") + @Operation( + summary = "스티커 사용 이력 저장", description = "보드에 사용한 스티커 ID를 배열에 담아 모두 보내주세요." + ) + @PostMapping("/use") + fun useSticker( + @RequestBody @Valid request: StickerUseRequest + ) : ApplicationResponse { + val user = SecurityContextHolder.getContext().authentication.principal as UserDto.Companion.Res + stickerService.use(request, user) + return ApplicationResponse.ok() + } + + @Tag(name = "1.5.0") + @Operation( + summary = "최근 사용한 스티커 조회", description = "최근 30일 이내에 사용한 스티커를 조회합니다." + ) + @GetMapping("/recent-use") + fun getRecentUseSticker() : ApplicationResponse> { + val user = SecurityContextHolder.getContext().authentication.principal as UserDto.Companion.Res + return ApplicationResponse.ok(stickerService.getRecentUse(user)) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/domain/sticker/dto/StickerUseRequest.kt b/src/main/kotlin/com/ddd/sonnypolabobe/domain/sticker/dto/StickerUseRequest.kt new file mode 100644 index 0000000..0e5ef9d --- /dev/null +++ b/src/main/kotlin/com/ddd/sonnypolabobe/domain/sticker/dto/StickerUseRequest.kt @@ -0,0 +1,13 @@ +package com.ddd.sonnypolabobe.domain.sticker.dto + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank + +@Schema(description = "스티커 사용 이력 저장") +data class StickerUseRequest( + @field:Schema(description = "스티커 ID 리스트", example = "[\"STK_0001\",\"STK_0001\"]") + val stickerIds: List, + @field:Schema(description = "게시글 ID", example = "adksjfldskjglaijg") + @field:NotBlank(message = "게시글 ID는 필수입니다.") + val boardId: String, +) diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/domain/sticker/repository/StickerJooqRepository.kt b/src/main/kotlin/com/ddd/sonnypolabobe/domain/sticker/repository/StickerJooqRepository.kt new file mode 100644 index 0000000..6ad8d16 --- /dev/null +++ b/src/main/kotlin/com/ddd/sonnypolabobe/domain/sticker/repository/StickerJooqRepository.kt @@ -0,0 +1,9 @@ +package com.ddd.sonnypolabobe.domain.sticker.repository + +import com.ddd.sonnypolabobe.domain.sticker.dto.StickerUseRequest +import com.ddd.sonnypolabobe.domain.user.dto.UserDto + +interface StickerJooqRepository { + fun insertAll(request: StickerUseRequest, user: UserDto.Companion.Res) + fun readAllByUserId(userId: Long): Set +} \ No newline at end of file diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/domain/sticker/repository/StickerJooqRepositoryImpl.kt b/src/main/kotlin/com/ddd/sonnypolabobe/domain/sticker/repository/StickerJooqRepositoryImpl.kt new file mode 100644 index 0000000..8607d66 --- /dev/null +++ b/src/main/kotlin/com/ddd/sonnypolabobe/domain/sticker/repository/StickerJooqRepositoryImpl.kt @@ -0,0 +1,39 @@ +package com.ddd.sonnypolabobe.domain.sticker.repository + +import com.ddd.sonnypolabobe.domain.sticker.dto.StickerUseRequest +import com.ddd.sonnypolabobe.domain.user.dto.UserDto +import com.ddd.sonnypolabobe.global.util.DateConverter +import com.ddd.sonnypolabobe.global.util.UuidConverter +import com.ddd.sonnypolabobe.jooq.polabo.tables.references.STICKER_USE_HISTORY +import org.jooq.DSLContext +import org.springframework.stereotype.Repository +import java.time.LocalDateTime + +@Repository +class StickerJooqRepositoryImpl(private val dslContext: DSLContext) : StickerJooqRepository{ + override fun insertAll(request: StickerUseRequest, user: UserDto.Companion.Res) { + dslContext.batchInsert( + request.stickerIds.map { + STICKER_USE_HISTORY.newRecord().apply { + this.userId = user.id + this.stickerId = it + this.boardId = UuidConverter.uuidToByteArray(UuidConverter.stringToUUID(request.boardId)) + this.createdAt = DateConverter.convertToKst(LocalDateTime.now()) + } + } + ).execute() + + } + + override fun readAllByUserId(userId: Long): Set { + return dslContext.select(STICKER_USE_HISTORY.STICKER_ID) + .from(STICKER_USE_HISTORY) + .where(STICKER_USE_HISTORY.USER_ID.eq(userId).and( + STICKER_USE_HISTORY.CREATED_AT.gt( + DateConverter.convertToKst(LocalDateTime.now().minusDays(30)) + ) + )) + .fetchInto(String::class.java) + .toSet() + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/domain/sticker/service/StickerService.kt b/src/main/kotlin/com/ddd/sonnypolabobe/domain/sticker/service/StickerService.kt new file mode 100644 index 0000000..9bc2182 --- /dev/null +++ b/src/main/kotlin/com/ddd/sonnypolabobe/domain/sticker/service/StickerService.kt @@ -0,0 +1,22 @@ +package com.ddd.sonnypolabobe.domain.sticker.service + +import com.ddd.sonnypolabobe.domain.sticker.dto.StickerUseRequest +import com.ddd.sonnypolabobe.domain.sticker.repository.StickerJooqRepository +import com.ddd.sonnypolabobe.domain.user.dto.UserDto +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +class StickerService( + private val stickerJooqRepository: StickerJooqRepository +) { + @Transactional + fun use(request: StickerUseRequest, user: UserDto.Companion.Res) { + stickerJooqRepository.insertAll(request, user) + } + + @Transactional(readOnly = true) + fun getRecentUse(user: UserDto.Companion.Res): Set { + return stickerJooqRepository.readAllByUserId(user.id) + } +} \ No newline at end of file diff --git a/src/test/kotlin/com/ddd/sonnypolabobe/SonnyPolaboBeApplicationTests.kt b/src/test/kotlin/com/ddd/sonnypolabobe/SonnyPolaboBeApplicationTests.kt index 76f28d1..09ef4bc 100644 --- a/src/test/kotlin/com/ddd/sonnypolabobe/SonnyPolaboBeApplicationTests.kt +++ b/src/test/kotlin/com/ddd/sonnypolabobe/SonnyPolaboBeApplicationTests.kt @@ -1,196 +1,6 @@ package com.ddd.sonnypolabobe -import org.junit.jupiter.api.Test -import org.springframework.boot.test.context.SpringBootTest -import java.util.* - //@SpringBootTest class SonnyPolaboBeApplicationTests { - @Test - fun contextLoads() { - println(solution(2, 10, intArrayOf(7,4,5,6))) // 8 -// println(solution(arrayOf( -// intArrayOf(1,2,1), -// intArrayOf(8,2,0), -// intArrayOf(1,7,2) -// ), intArrayOf(0, 0))) // true -// println(solution(arrayOf( -// intArrayOf(1,2,3,2,1), -// intArrayOf(4,2,0,7,2), -// intArrayOf(1,3,3,8,1), -// intArrayOf(2,0,1,1,1), -// intArrayOf(8,2,8,1,1) -// ), intArrayOf(0, 0))) // false - -// println(solution(arrayOf( -// intArrayOf(1,2,3,2,1), -// intArrayOf(4,2,0,7,1), -// intArrayOf(1,3,2,8,1), -// intArrayOf(2,0,1,1,1), -// intArrayOf(8,2,1,2,1) -// ), intArrayOf(4,3) -// )) // true -// println(solution( -// intArrayOf(23), // 고객 수 -// intArrayOf(12, 3, 19), // 모델 처리량 -// intArrayOf(28, 10, 35) // 모델 비용 -// )) - - } - - fun solution(bridge_length: Int, weight: Int, truck_weights: IntArray): Int { - var answer = 0 - - val queue: Queue = LinkedList() - var totalWeight = 0 - - for(truck in truck_weights) { - queue.add(truck) - } - - val bridge = LinkedList(List(bridge_length) { 0 }) - - while(bridge.isNotEmpty()) { - answer++ - - totalWeight -= bridge.poll() // 다리를 건넌 트럭의 무게를 빼준다. - - if(queue.isNotEmpty()) { - val nextWeight = queue.peek() - if(nextWeight + totalWeight <= weight ) { - totalWeight += nextWeight - bridge.add(queue.poll()) - } else { - bridge.add(0) - } - } - - - } - - -// while (bridge.isNotEmpty()) { -// answer++ -// totalWeight -= bridge.poll() -// -// if (waiting.isNotEmpty()) { -// val nextWeight = waiting.peek() -// -// if (totalWeight + nextWeight <= weight) { -// totalWeight += nextWeight -// bridge.add(waiting.poll()) -// } else { -// bridge.add(0) -// } -// } -// } - return answer - } - - -// fun solution(map : Array, entrancePoint: IntArray) : Boolean { -// // 출발점에서 닭가슴살을 찾을 수 있는지 여부가 answer -// // 닭가슴살은 7 -// // 액상 과당은 0, 초콜릿은 8 이라고 할 때 -// // map에서 출발점을 기준으로는 좌우로만 갈 수 있다. -// // 이후에는 위 아래로 갈 수 있다. -// // 그 다음에는 좌우로만 갈 수 있다. 이 구성을 반복한다고 할 때 이동 방향으로는 현재 위치의 숫자만큼 간다. -// // 이동의 도착점에 액상 과당이나 초콜릿이 있다면 false를 반환한다. -// // 영역을 벗어나도 false를 반환한다. -// -// var answer = false -// var x = entrancePoint[0] -// var y = entrancePoint[1] -// var direction = if (x % 2 == 0) 0 else 2 -// var nextX = 0 -// var nextY = 0 -// -// while (true) { -// if (map[x][y] == 7) { -// answer = true -// break -// } -// if (map[x][y] == 0 || map[x][y] == 8) { -// break -// } -// when (direction) { -// 0 -> { -// nextX = x -// nextY = y + map[x][y] -// } -// 1 -> { -// nextX = x + map[x][y] -// nextY = y -// } -// 2 -> { -// nextX = x -// nextY = y - map[x][y] -// } -// 3 -> { -// nextX = x - map[x][y] -// nextY = y -// } -// } -// if (nextX < 0 || nextX >= map.size || nextY < 0 || nextY >= map[0].size) { -// break -// } -// x = nextX -// y = nextY -// direction = (direction + 1) % 4 // 방향을 바꾼다. -// } -// return answer -// -// } - -// fun solution(customers : IntArray, modelCapacities: IntArray, modelCosts: IntArray) : Int { -// var answer = 0 -// -// // 매 시간 고객의 접수 건을 담고 있는 배열 customers -// // 각 모델의 처리량을 담고 있는 배열 modelCapacities 예를 들면, A-12, B-3, C-19 -// // 각 모델의 비용을 담고 있는 배열 modelCosts 예를 들면, A-28, B-10, C-35 -// // 각 모델은 1시간에 위 처리량만큼 처리할 수 있다. -// -// // 최소 비용으로 처리하고자 할 때, 그 비용을 반환한다. -// -// // 모델의 처리량과 비용을 (처리량, 비용) 쌍으로 묶고, 처리량이 큰 순으로 정렬 -// val pair = modelCapacities.zip(modelCosts).sortedByDescending { it.first } -// -// for (customer in customers) { -// var minCost = Int.MAX_VALUE -// -// for (i in pair.indices) { -// val (capacity, cost) = pair[i] -// val fullModelsNeeded = customer / capacity -// val remainder = customer % capacity -// -// // 총 비용 계산 -// var totalCost = cost * fullModelsNeeded -// -// // 잔여 고객 처리 비용 추가 -// if (remainder > 0) { -// // 잔여 고객을 처리하기 위한 최소 비용을 계산 -// var extraCost = Int.MAX_VALUE -// for (j in pair.indices) { -// val (extraCapacity, extraCostValue) = pair[j] -// if (extraCapacity >= remainder) { -// extraCost = minOf(extraCost, extraCostValue) -// } -// } -// totalCost += extraCost -// } -// -// // 최소 비용 업데이트 -// minCost = minOf(minCost, totalCost) -// } -// answer += minCost -// } -// -// return answer -// } - - - - - }