Skip to content

Commit

Permalink
Merge pull request #41 from DDD-Community/feature/POLABO-130
Browse files Browse the repository at this point in the history
feat: 5차 MVP 기능 개발
  • Loading branch information
dldmsql authored Oct 30, 2024
2 parents 80846f3 + fc36d77 commit 87770f2
Show file tree
Hide file tree
Showing 20 changed files with 145 additions and 82 deletions.
15 changes: 3 additions & 12 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ plugins {
id("io.spring.dependency-management") version "1.1.5"
kotlin("jvm") version kotlinVersion
kotlin("plugin.spring") version kotlinVersion
// kotlin("plugin.jpa") version kotlinVersion
// kotlin("plugin.allopen") version kotlinVersion
kotlin("kapt") version kotlinVersion
id("nu.studer.jooq") version "9.0"
}
Expand All @@ -23,7 +21,7 @@ java {
}

jooq {
version.set("3.18.10")
version.set("3.19.0")
edition.set(JooqEdition.OSS)

configurations {
Expand Down Expand Up @@ -59,12 +57,6 @@ jooq {
}
}

//allOpen {
// annotation("jakarta.persistence.Entity")
// annotation("jakarta.persistence.MappedSuperclass")
// annotation("jakarta.persistence.Embeddable")
//}

repositories {
mavenCentral()
}
Expand All @@ -73,15 +65,14 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0")
// implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.jetbrains.kotlin:kotlin-reflect")
runtimeOnly("com.mysql:mysql-connector-j")
implementation("org.springframework.boot:spring-boot-starter-jooq")
jooqGenerator("com.mysql:mysql-connector-j")
jooqGenerator("org.jooq:jooq-meta:3.18.10")
jooqGenerator("org.jooq:jooq-codegen:3.18.10")
jooqGenerator("org.jooq:jooq-meta:3.19.0")
jooqGenerator("org.jooq:jooq-codegen:3.19.0")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,18 @@ import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.web.bind.annotation.*
import java.util.UUID

@Tag(name = "Board API", description = "보드 관련 API")
@RestController
@RequestMapping("/api/v1/boards")
class BoardController(
private val boardService: BoardService,
private val jwtUtil: JwtUtil
) {
@Tag(name = "1.4.0")
@Operation(
summary = "보드 생성", description = """
보드를 생성합니다.
userId는 추후 회원가입 기능이 추가될 것을 대비한 것입니다. 지금은 null로 주세요.
userId 데이터는 백에서 채울 것입니다.!
options 필드 추가했습니다. 폴라로이드 옵션과 동일하게 구성했습니다.
key : THEMA, value : 프론트에서 지정한 숫자 혹은 식별값
"""
)
@PostMapping
Expand All @@ -35,11 +34,11 @@ class BoardController(
return ApplicationResponse.ok(this.boardService.create(request))
}

@Tag(name = "1.3.0")
@Tag(name = "1.4.0")
@Operation(
summary = "보드 조회", description = """
보드를 조회합니다.
DTO 필드 수정했습니다. 스티커 리스트 추가했습니다.
DTO 필드 수정했습니다. 옵션이 추가되었습니다.
"""
)
Expand All @@ -51,6 +50,7 @@ class BoardController(
return ApplicationResponse.ok(this.boardService.getById(id, user))
}

@Tag(name = "1.0.0")
@Operation(
summary = "보드 누적 생성 수 조회", description = """
보드 누적 생성 수를 조회합니다.
Expand All @@ -59,6 +59,7 @@ class BoardController(
@GetMapping("/total-count")
fun getTotalCount() = ApplicationResponse.ok(this.boardService.getTotalCount())

@Tag(name = "1.0.0")
@Operation(
summary = "오늘 생성 가능한 보드 수 조회", description = """
오늘 생성 가능한 보드 수를 조회합니다.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package com.ddd.sonnypolabobe.domain.board.controller.dto

import com.ddd.sonnypolabobe.domain.polaroid.enumerate.ExtraOption
import io.swagger.v3.oas.annotations.media.Schema
import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.Pattern
import java.util.*

data class BoardCreateRequest(
@field:Schema(description = "제목", example = "쏘니의 보드")
@field:NotBlank
@field:Pattern(regexp = "^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*()_+=-])(?=.*[ㄱ-ㅎㅏ-ㅣ가-힣]).{1,20}$", message = "제목은 국문, 영문, 숫자, 특수문자, 띄어쓰기를 포함한 20자 이내여야 합니다.")
val title: String,
@field:Schema(description = "작성자 아이디", example = "null", required = false)
var userId: Long? = null
var userId: Long? = null,
@field:Schema(description = "보드 옵션 - key 값으로 THEMA를 주세요. value로는 프론트에서 지정한 숫자 혹은 식별값을 주세요.", example = "{\"THEMA\":\"value3\"}")
val options : Map<ExtraOption, String>
)
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.ddd.sonnypolabobe.domain.board.controller.dto

import com.ddd.sonnypolabobe.domain.polaroid.dto.PolaroidGetResponse
import com.ddd.sonnypolabobe.domain.polaroid.enumerate.ExtraOption
import io.swagger.v3.oas.annotations.media.Schema

data class BoardGetResponse(
Expand All @@ -9,5 +10,7 @@ data class BoardGetResponse(
@field:Schema(description = "폴라로이드")
val items: List<PolaroidGetResponse>,
@field:Schema(description = "작성자 여부", example = "true")
val isMine : Boolean
val isMine : Boolean,
@field:Schema(description = "옵션", example = "{\"THEMA\":\"value3\"}")
val options : Map<ExtraOption, String>?
)
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.*
@RequestMapping("/api/v1/my/boards")
class MyBoardController(private val myBoardService: MyBoardService) {

@Tag(name = "1.0.0")
@Operation(
summary = "내 보드 목록 조회", description = """
내 보드 목록을 조회합니다.
Expand All @@ -30,6 +31,7 @@ class MyBoardController(private val myBoardService: MyBoardService) {
}


@Tag(name = "1.0.0")
@Operation(
summary = "내 보드 이름 수정",
description = """
Expand All @@ -47,6 +49,7 @@ class MyBoardController(private val myBoardService: MyBoardService) {
return ApplicationResponse.ok()
}

@Tag(name = "1.1.0")
@Operation(
summary = "내 보드 삭제",
description = """
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.ddd.sonnypolabobe.domain.board.repository

import com.ddd.sonnypolabobe.domain.board.controller.dto.BoardCreateRequest
import com.ddd.sonnypolabobe.domain.board.controller.dto.BoardGetResponse
import com.ddd.sonnypolabobe.domain.board.my.dto.MyBoardDto
import com.ddd.sonnypolabobe.domain.board.repository.vo.BoardGetOneVo
import com.ddd.sonnypolabobe.domain.user.dto.GenderType
Expand All @@ -10,14 +9,14 @@ import com.ddd.sonnypolabobe.global.util.UuidConverter
import com.ddd.sonnypolabobe.global.util.UuidGenerator
import com.ddd.sonnypolabobe.jooq.polabo.enums.UserGender
import com.ddd.sonnypolabobe.jooq.polabo.tables.Board
import com.ddd.sonnypolabobe.jooq.polabo.tables.BoardSticker
import com.ddd.sonnypolabobe.jooq.polabo.tables.Polaroid
import com.ddd.sonnypolabobe.jooq.polabo.tables.User
import org.jooq.*
import com.fasterxml.jackson.databind.ObjectMapper
import org.jooq.Condition
import org.jooq.DSLContext
import org.jooq.impl.DSL
import org.jooq.impl.DSL.*
import org.springframework.stereotype.Repository
import java.sql.Timestamp
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.temporal.ChronoUnit
Expand All @@ -37,6 +36,7 @@ class BoardJooqRepositoryImpl(
this.yn = 1
this.activeyn = 1
this.userId = request.userId
this.options = request.options.let { ObjectMapper().writeValueAsString(it) }
}
val result = this.dslContext.insertInto(jBoard)
.set(insertValue)
Expand All @@ -51,16 +51,17 @@ class BoardJooqRepositoryImpl(

return this.dslContext
.select(
jBoard.ID.convertFrom { it?.let{UuidConverter.byteArrayToUUID(it) } },
jBoard.ID.convertFrom { it?.let { UuidConverter.byteArrayToUUID(it) } },
jBoard.TITLE,
jBoard.OPTIONS.`as`(BoardGetOneVo::options.name),
jBoard.USER_ID.`as`(BoardGetOneVo::ownerId.name),
jPolaroid.ID.`as`(BoardGetOneVo::polaroidId.name),
jPolaroid.IMAGE_KEY,
jPolaroid.ONE_LINE_MESSAGE,
jPolaroid.CREATED_AT,
jPolaroid.USER_ID,
jPolaroid.NICKNAME,
jPolaroid.OPTIONS
jPolaroid.OPTIONS.`as`(BoardGetOneVo::polaroidOptions.name)
)
.from(jBoard)
.leftJoin(jPolaroid).on(
Expand Down Expand Up @@ -90,11 +91,21 @@ class BoardJooqRepositoryImpl(
.selectCount()
.from(jBoard)
.where(
jBoard.CREATED_AT.greaterOrEqual(DateConverter.convertToKst(LocalDateTime.now().withHour(0).withMinute(0).withSecond(0)))
.and(jBoard.CREATED_AT.lessThan(DateConverter.convertToKst(LocalDateTime.now().withHour(23).withMinute(59).withSecond(59)))
.and(jBoard.YN.eq(1))
.and(jBoard.ACTIVEYN.eq(1))
))
jBoard.CREATED_AT.greaterOrEqual(
DateConverter.convertToKst(
LocalDateTime.now().withHour(0).withMinute(0).withSecond(0)
)
)
.and(
jBoard.CREATED_AT.lessThan(
DateConverter.convertToKst(
LocalDateTime.now().withHour(23).withMinute(59).withSecond(59)
)
)
.and(jBoard.YN.eq(1))
.and(jBoard.ACTIVEYN.eq(1))
)
)
.fetchOne(0, Long::class.java) ?: 0L
}

Expand Down Expand Up @@ -173,9 +184,9 @@ class BoardJooqRepositoryImpl(
val jPolaroid = Polaroid.POLAROID
val boardSubQuery =
this.dslContext.select(jBoard.ID.`as`("id"))
.from(jBoard)
.where(jBoard.USER_ID.eq(userId).and(jBoard.YN.eq(1)).and(jBoard.ACTIVEYN.eq(1)))
.asTable()
.from(jBoard)
.where(jBoard.USER_ID.eq(userId).and(jBoard.YN.eq(1)).and(jBoard.ACTIVEYN.eq(1)))
.asTable()

val data = this.dslContext.select(
jBoard.ID,
Expand All @@ -187,22 +198,24 @@ class BoardJooqRepositoryImpl(
jBoard.ID.eq(jPolaroid.BOARD_ID).and(jPolaroid.USER_ID.eq(userId))
.and(jPolaroid.YN.eq(1)).and(jPolaroid.ACTIVEYN.eq(1))
)
.where(jBoard.ID.notIn(
this.dslContext.select(boardSubQuery.field("id", ByteArray::class.java))
.from(boardSubQuery)
))
.where(
jBoard.ID.notIn(
this.dslContext.select(boardSubQuery.field("id", ByteArray::class.java))
.from(boardSubQuery)
)
)
.orderBy(jBoard.CREATED_AT.desc())
.limit(size)
.offset(page)

return data
.map {
MyBoardDto.Companion.PageListRes(
id = UuidConverter.byteArrayToUUID(it.get("id", ByteArray::class.java)!!),
title = it.get("title", String::class.java)!!,
createdAt = it.get("created_at", LocalDateTime::class.java)!!,
)
}.distinct()
MyBoardDto.Companion.PageListRes(
id = UuidConverter.byteArrayToUUID(it.get("id", ByteArray::class.java)!!),
title = it.get("title", String::class.java)!!,
createdAt = it.get("created_at", LocalDateTime::class.java)!!,
)
}.distinct()
}

override fun selectTotalCountByParticipant(userId: Long): Long {
Expand All @@ -220,10 +233,12 @@ class BoardJooqRepositoryImpl(
jBoard.ID.eq(jPolaroid.BOARD_ID).and(jPolaroid.USER_ID.eq(userId))
.and(jPolaroid.YN.eq(1)).and(jPolaroid.ACTIVEYN.eq(1))
)
.where(jBoard.ID.notIn(
this.dslContext.select(boardSubQuery.field("id", ByteArray::class.java))
.from(boardSubQuery)
))
.where(
jBoard.ID.notIn(
this.dslContext.select(boardSubQuery.field("id", ByteArray::class.java))
.from(boardSubQuery)
)
)
.fetchOne(0, Long::class.java)
?: 0L
}
Expand All @@ -233,7 +248,7 @@ class BoardJooqRepositoryImpl(
val jUser = User.USER
val jPolaroid = Polaroid.POLAROID
// 현재 날짜 기준으로 연령대를 계산하는 로직
var userAgeGroup : String = "20-29세"
var userAgeGroup: String = "20-29세"
if (userBirth != null) {
val age = ChronoUnit.YEARS.between(userBirth, LocalDate.now())
userAgeGroup = if (age < 15) {
Expand Down Expand Up @@ -264,37 +279,43 @@ class BoardJooqRepositoryImpl(
.leftJoin(
this.dslContext.select(jPolaroid.BOARD_ID, count().`as`("polaroid_count"))
.from(jPolaroid)
.where(jPolaroid.YN.eq(1)
.and(jPolaroid.ACTIVEYN.eq(1)))
.where(
jPolaroid.YN.eq(1)
.and(jPolaroid.ACTIVEYN.eq(1))
)
.groupBy(jPolaroid.BOARD_ID)
.asTable("sub_query")
)
.on(jBoard.ID.eq(field(name("sub_query", "board_id"), jBoard.ID.dataType)))
.where(jBoard.YN.eq(1)
.and(jBoard.ACTIVEYN.eq(1))
.and(jBoard.CREATED_AT.greaterOrEqual(thirtyDaysAgo))
.and(genderAndAgeGroupMatch(userGender, userAgeGroup))
)
.orderBy(field("sub_query.polaroid_count", Int::class.java).desc(), jBoard.CREATED_AT.desc())
.where(
jBoard.YN.eq(1)
.and(jBoard.ACTIVEYN.eq(1))
.and(jBoard.CREATED_AT.greaterOrEqual(thirtyDaysAgo))
.and(genderAndAgeGroupMatch(userGender, userAgeGroup))
)
.orderBy(
field("sub_query.polaroid_count", Int::class.java).desc(),
jBoard.CREATED_AT.desc()
)
.limit(16)
.fetchInto(String::class.java)
}

// 성별 및 연령대 일치 조건을 위한 메서드
private fun genderAndAgeGroupMatch( userGender : GenderType, userAgeGroup: String?): Condition {
private fun genderAndAgeGroupMatch(userGender: GenderType, userAgeGroup: String?): Condition {
return User.USER.GENDER.eq(UserGender.valueOf(userGender.name))
.or(User.USER.BIRTH_DT.isNotNull().and(ageGroupCondition(userAgeGroup)))
}

// 연령대 계산 로직에 따른 조건을 처리하는 메서드
private fun ageGroupCondition(ageGroup: String?) : Condition{
private fun ageGroupCondition(ageGroup: String?): Condition {
return `when`(User.USER.BIRTH_DT.ge(LocalDate.now().minusYears(15)), "15세 미만")
.`when`(User.USER.BIRTH_DT.ge(LocalDate.now().minusYears(19)), "15-19세")
.`when`(User.USER.BIRTH_DT.ge(LocalDate.now().minusYears(29)), "20-29세")
.`when`(User.USER.BIRTH_DT.ge(LocalDate.now().minusYears(39)), "30-39세")
.`when`(User.USER.BIRTH_DT.ge(LocalDate.now().minusYears(49)), "40-49세")
.`when`(User.USER.BIRTH_DT.ge(LocalDate.now().minusYears(59)), "50-59세")
.otherwise("60대 이상").eq(ageGroup);
.`when`(User.USER.BIRTH_DT.ge(LocalDate.now().minusYears(19)), "15-19세")
.`when`(User.USER.BIRTH_DT.ge(LocalDate.now().minusYears(29)), "20-29세")
.`when`(User.USER.BIRTH_DT.ge(LocalDate.now().minusYears(39)), "30-39세")
.`when`(User.USER.BIRTH_DT.ge(LocalDate.now().minusYears(49)), "40-49세")
.`when`(User.USER.BIRTH_DT.ge(LocalDate.now().minusYears(59)), "50-59세")
.otherwise("60대 이상").eq(ageGroup);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import java.util.UUID
data class BoardGetOneVo(
val id: UUID?,
val title: String?,
val options: String?,
val ownerId: Long?,
val polaroidId : Long?,
val imageKey : String?,
val oneLineMessage: String?,
val createdAt: LocalDateTime?,
val userId : Long?,
val nickname: String?,
val options: String?
val polaroidOptions: String?
)
Loading

0 comments on commit 87770f2

Please sign in to comment.