Skip to content

Commit fc36d77

Browse files
committed
feat: 5차 MVP 기능 개발
1 parent 2fda1bf commit fc36d77

20 files changed

+145
-82
lines changed

build.gradle.kts

+3-12
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ plugins {
77
id("io.spring.dependency-management") version "1.1.5"
88
kotlin("jvm") version kotlinVersion
99
kotlin("plugin.spring") version kotlinVersion
10-
// kotlin("plugin.jpa") version kotlinVersion
11-
// kotlin("plugin.allopen") version kotlinVersion
1210
kotlin("kapt") version kotlinVersion
1311
id("nu.studer.jooq") version "9.0"
1412
}
@@ -23,7 +21,7 @@ java {
2321
}
2422

2523
jooq {
26-
version.set("3.18.10")
24+
version.set("3.19.0")
2725
edition.set(JooqEdition.OSS)
2826

2927
configurations {
@@ -59,12 +57,6 @@ jooq {
5957
}
6058
}
6159

62-
//allOpen {
63-
// annotation("jakarta.persistence.Entity")
64-
// annotation("jakarta.persistence.MappedSuperclass")
65-
// annotation("jakarta.persistence.Embeddable")
66-
//}
67-
6860
repositories {
6961
mavenCentral()
7062
}
@@ -73,15 +65,14 @@ dependencies {
7365
implementation("org.springframework.boot:spring-boot-starter")
7466
implementation("org.springframework.boot:spring-boot-starter-web")
7567
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0")
76-
// implementation("org.springframework.boot:spring-boot-starter-data-jpa")
7768
implementation("org.springframework.boot:spring-boot-starter-webflux")
7869
implementation("org.springframework.boot:spring-boot-starter-actuator")
7970
implementation("org.jetbrains.kotlin:kotlin-reflect")
8071
runtimeOnly("com.mysql:mysql-connector-j")
8172
implementation("org.springframework.boot:spring-boot-starter-jooq")
8273
jooqGenerator("com.mysql:mysql-connector-j")
83-
jooqGenerator("org.jooq:jooq-meta:3.18.10")
84-
jooqGenerator("org.jooq:jooq-codegen:3.18.10")
74+
jooqGenerator("org.jooq:jooq-meta:3.19.0")
75+
jooqGenerator("org.jooq:jooq-codegen:3.19.0")
8576
testImplementation("org.springframework.boot:spring-boot-starter-test")
8677
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
8778
testRuntimeOnly("org.junit.platform:junit-platform-launcher")

src/main/kotlin/com/ddd/sonnypolabobe/domain/board/controller/BoardController.kt

+7-6
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,18 @@ import org.springframework.security.core.context.SecurityContextHolder
1212
import org.springframework.web.bind.annotation.*
1313
import java.util.UUID
1414

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

38-
@Tag(name = "1.3.0")
37+
@Tag(name = "1.4.0")
3938
@Operation(
4039
summary = "보드 조회", description = """
4140
보드를 조회합니다.
42-
DTO 필드 수정했습니다. 스티커 리스트 추가했습니다.
41+
DTO 필드 수정했습니다. 옵션이 추가되었습니다.
4342
4443
"""
4544
)
@@ -51,6 +50,7 @@ class BoardController(
5150
return ApplicationResponse.ok(this.boardService.getById(id, user))
5251
}
5352

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

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

3+
import com.ddd.sonnypolabobe.domain.polaroid.enumerate.ExtraOption
34
import io.swagger.v3.oas.annotations.media.Schema
45
import jakarta.validation.constraints.NotBlank
56
import jakarta.validation.constraints.Pattern
6-
import java.util.*
77

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

33
import com.ddd.sonnypolabobe.domain.polaroid.dto.PolaroidGetResponse
4+
import com.ddd.sonnypolabobe.domain.polaroid.enumerate.ExtraOption
45
import io.swagger.v3.oas.annotations.media.Schema
56

67
data class BoardGetResponse(
@@ -9,5 +10,7 @@ data class BoardGetResponse(
910
@field:Schema(description = "폴라로이드")
1011
val items: List<PolaroidGetResponse>,
1112
@field:Schema(description = "작성자 여부", example = "true")
12-
val isMine : Boolean
13+
val isMine : Boolean,
14+
@field:Schema(description = "옵션", example = "{\"THEMA\":\"value3\"}")
15+
val options : Map<ExtraOption, String>?
1316
)

src/main/kotlin/com/ddd/sonnypolabobe/domain/board/my/controller/MyBoardController.kt

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.*
1414
@RequestMapping("/api/v1/my/boards")
1515
class MyBoardController(private val myBoardService: MyBoardService) {
1616

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

3233

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

52+
@Tag(name = "1.1.0")
5053
@Operation(
5154
summary = "내 보드 삭제",
5255
description = """

src/main/kotlin/com/ddd/sonnypolabobe/domain/board/repository/BoardJooqRepositoryImpl.kt

+66-45
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.ddd.sonnypolabobe.domain.board.repository
22

33
import com.ddd.sonnypolabobe.domain.board.controller.dto.BoardCreateRequest
4-
import com.ddd.sonnypolabobe.domain.board.controller.dto.BoardGetResponse
54
import com.ddd.sonnypolabobe.domain.board.my.dto.MyBoardDto
65
import com.ddd.sonnypolabobe.domain.board.repository.vo.BoardGetOneVo
76
import com.ddd.sonnypolabobe.domain.user.dto.GenderType
@@ -10,14 +9,14 @@ import com.ddd.sonnypolabobe.global.util.UuidConverter
109
import com.ddd.sonnypolabobe.global.util.UuidGenerator
1110
import com.ddd.sonnypolabobe.jooq.polabo.enums.UserGender
1211
import com.ddd.sonnypolabobe.jooq.polabo.tables.Board
13-
import com.ddd.sonnypolabobe.jooq.polabo.tables.BoardSticker
1412
import com.ddd.sonnypolabobe.jooq.polabo.tables.Polaroid
1513
import com.ddd.sonnypolabobe.jooq.polabo.tables.User
16-
import org.jooq.*
14+
import com.fasterxml.jackson.databind.ObjectMapper
15+
import org.jooq.Condition
16+
import org.jooq.DSLContext
1717
import org.jooq.impl.DSL
1818
import org.jooq.impl.DSL.*
1919
import org.springframework.stereotype.Repository
20-
import java.sql.Timestamp
2120
import java.time.LocalDate
2221
import java.time.LocalDateTime
2322
import java.time.temporal.ChronoUnit
@@ -37,6 +36,7 @@ class BoardJooqRepositoryImpl(
3736
this.yn = 1
3837
this.activeyn = 1
3938
this.userId = request.userId
39+
this.options = request.options.let { ObjectMapper().writeValueAsString(it) }
4040
}
4141
val result = this.dslContext.insertInto(jBoard)
4242
.set(insertValue)
@@ -51,16 +51,17 @@ class BoardJooqRepositoryImpl(
5151

5252
return this.dslContext
5353
.select(
54-
jBoard.ID.convertFrom { it?.let{UuidConverter.byteArrayToUUID(it) } },
54+
jBoard.ID.convertFrom { it?.let { UuidConverter.byteArrayToUUID(it) } },
5555
jBoard.TITLE,
56+
jBoard.OPTIONS.`as`(BoardGetOneVo::options.name),
5657
jBoard.USER_ID.`as`(BoardGetOneVo::ownerId.name),
5758
jPolaroid.ID.`as`(BoardGetOneVo::polaroidId.name),
5859
jPolaroid.IMAGE_KEY,
5960
jPolaroid.ONE_LINE_MESSAGE,
6061
jPolaroid.CREATED_AT,
6162
jPolaroid.USER_ID,
6263
jPolaroid.NICKNAME,
63-
jPolaroid.OPTIONS
64+
jPolaroid.OPTIONS.`as`(BoardGetOneVo::polaroidOptions.name)
6465
)
6566
.from(jBoard)
6667
.leftJoin(jPolaroid).on(
@@ -90,11 +91,21 @@ class BoardJooqRepositoryImpl(
9091
.selectCount()
9192
.from(jBoard)
9293
.where(
93-
jBoard.CREATED_AT.greaterOrEqual(DateConverter.convertToKst(LocalDateTime.now().withHour(0).withMinute(0).withSecond(0)))
94-
.and(jBoard.CREATED_AT.lessThan(DateConverter.convertToKst(LocalDateTime.now().withHour(23).withMinute(59).withSecond(59)))
95-
.and(jBoard.YN.eq(1))
96-
.and(jBoard.ACTIVEYN.eq(1))
97-
))
94+
jBoard.CREATED_AT.greaterOrEqual(
95+
DateConverter.convertToKst(
96+
LocalDateTime.now().withHour(0).withMinute(0).withSecond(0)
97+
)
98+
)
99+
.and(
100+
jBoard.CREATED_AT.lessThan(
101+
DateConverter.convertToKst(
102+
LocalDateTime.now().withHour(23).withMinute(59).withSecond(59)
103+
)
104+
)
105+
.and(jBoard.YN.eq(1))
106+
.and(jBoard.ACTIVEYN.eq(1))
107+
)
108+
)
98109
.fetchOne(0, Long::class.java) ?: 0L
99110
}
100111

@@ -173,9 +184,9 @@ class BoardJooqRepositoryImpl(
173184
val jPolaroid = Polaroid.POLAROID
174185
val boardSubQuery =
175186
this.dslContext.select(jBoard.ID.`as`("id"))
176-
.from(jBoard)
177-
.where(jBoard.USER_ID.eq(userId).and(jBoard.YN.eq(1)).and(jBoard.ACTIVEYN.eq(1)))
178-
.asTable()
187+
.from(jBoard)
188+
.where(jBoard.USER_ID.eq(userId).and(jBoard.YN.eq(1)).and(jBoard.ACTIVEYN.eq(1)))
189+
.asTable()
179190

180191
val data = this.dslContext.select(
181192
jBoard.ID,
@@ -187,22 +198,24 @@ class BoardJooqRepositoryImpl(
187198
jBoard.ID.eq(jPolaroid.BOARD_ID).and(jPolaroid.USER_ID.eq(userId))
188199
.and(jPolaroid.YN.eq(1)).and(jPolaroid.ACTIVEYN.eq(1))
189200
)
190-
.where(jBoard.ID.notIn(
191-
this.dslContext.select(boardSubQuery.field("id", ByteArray::class.java))
192-
.from(boardSubQuery)
193-
))
201+
.where(
202+
jBoard.ID.notIn(
203+
this.dslContext.select(boardSubQuery.field("id", ByteArray::class.java))
204+
.from(boardSubQuery)
205+
)
206+
)
194207
.orderBy(jBoard.CREATED_AT.desc())
195208
.limit(size)
196209
.offset(page)
197210

198211
return data
199212
.map {
200-
MyBoardDto.Companion.PageListRes(
201-
id = UuidConverter.byteArrayToUUID(it.get("id", ByteArray::class.java)!!),
202-
title = it.get("title", String::class.java)!!,
203-
createdAt = it.get("created_at", LocalDateTime::class.java)!!,
204-
)
205-
}.distinct()
213+
MyBoardDto.Companion.PageListRes(
214+
id = UuidConverter.byteArrayToUUID(it.get("id", ByteArray::class.java)!!),
215+
title = it.get("title", String::class.java)!!,
216+
createdAt = it.get("created_at", LocalDateTime::class.java)!!,
217+
)
218+
}.distinct()
206219
}
207220

208221
override fun selectTotalCountByParticipant(userId: Long): Long {
@@ -220,10 +233,12 @@ class BoardJooqRepositoryImpl(
220233
jBoard.ID.eq(jPolaroid.BOARD_ID).and(jPolaroid.USER_ID.eq(userId))
221234
.and(jPolaroid.YN.eq(1)).and(jPolaroid.ACTIVEYN.eq(1))
222235
)
223-
.where(jBoard.ID.notIn(
224-
this.dslContext.select(boardSubQuery.field("id", ByteArray::class.java))
225-
.from(boardSubQuery)
226-
))
236+
.where(
237+
jBoard.ID.notIn(
238+
this.dslContext.select(boardSubQuery.field("id", ByteArray::class.java))
239+
.from(boardSubQuery)
240+
)
241+
)
227242
.fetchOne(0, Long::class.java)
228243
?: 0L
229244
}
@@ -233,7 +248,7 @@ class BoardJooqRepositoryImpl(
233248
val jUser = User.USER
234249
val jPolaroid = Polaroid.POLAROID
235250
// 현재 날짜 기준으로 연령대를 계산하는 로직
236-
var userAgeGroup : String = "20-29세"
251+
var userAgeGroup: String = "20-29세"
237252
if (userBirth != null) {
238253
val age = ChronoUnit.YEARS.between(userBirth, LocalDate.now())
239254
userAgeGroup = if (age < 15) {
@@ -264,37 +279,43 @@ class BoardJooqRepositoryImpl(
264279
.leftJoin(
265280
this.dslContext.select(jPolaroid.BOARD_ID, count().`as`("polaroid_count"))
266281
.from(jPolaroid)
267-
.where(jPolaroid.YN.eq(1)
268-
.and(jPolaroid.ACTIVEYN.eq(1)))
282+
.where(
283+
jPolaroid.YN.eq(1)
284+
.and(jPolaroid.ACTIVEYN.eq(1))
285+
)
269286
.groupBy(jPolaroid.BOARD_ID)
270287
.asTable("sub_query")
271288
)
272289
.on(jBoard.ID.eq(field(name("sub_query", "board_id"), jBoard.ID.dataType)))
273-
.where(jBoard.YN.eq(1)
274-
.and(jBoard.ACTIVEYN.eq(1))
275-
.and(jBoard.CREATED_AT.greaterOrEqual(thirtyDaysAgo))
276-
.and(genderAndAgeGroupMatch(userGender, userAgeGroup))
277-
)
278-
.orderBy(field("sub_query.polaroid_count", Int::class.java).desc(), jBoard.CREATED_AT.desc())
290+
.where(
291+
jBoard.YN.eq(1)
292+
.and(jBoard.ACTIVEYN.eq(1))
293+
.and(jBoard.CREATED_AT.greaterOrEqual(thirtyDaysAgo))
294+
.and(genderAndAgeGroupMatch(userGender, userAgeGroup))
295+
)
296+
.orderBy(
297+
field("sub_query.polaroid_count", Int::class.java).desc(),
298+
jBoard.CREATED_AT.desc()
299+
)
279300
.limit(16)
280301
.fetchInto(String::class.java)
281302
}
282303

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

289310
// 연령대 계산 로직에 따른 조건을 처리하는 메서드
290-
private fun ageGroupCondition(ageGroup: String?) : Condition{
311+
private fun ageGroupCondition(ageGroup: String?): Condition {
291312
return `when`(User.USER.BIRTH_DT.ge(LocalDate.now().minusYears(15)), "15세 미만")
292-
.`when`(User.USER.BIRTH_DT.ge(LocalDate.now().minusYears(19)), "15-19세")
293-
.`when`(User.USER.BIRTH_DT.ge(LocalDate.now().minusYears(29)), "20-29세")
294-
.`when`(User.USER.BIRTH_DT.ge(LocalDate.now().minusYears(39)), "30-39세")
295-
.`when`(User.USER.BIRTH_DT.ge(LocalDate.now().minusYears(49)), "40-49세")
296-
.`when`(User.USER.BIRTH_DT.ge(LocalDate.now().minusYears(59)), "50-59세")
297-
.otherwise("60대 이상").eq(ageGroup);
313+
.`when`(User.USER.BIRTH_DT.ge(LocalDate.now().minusYears(19)), "15-19세")
314+
.`when`(User.USER.BIRTH_DT.ge(LocalDate.now().minusYears(29)), "20-29세")
315+
.`when`(User.USER.BIRTH_DT.ge(LocalDate.now().minusYears(39)), "30-39세")
316+
.`when`(User.USER.BIRTH_DT.ge(LocalDate.now().minusYears(49)), "40-49세")
317+
.`when`(User.USER.BIRTH_DT.ge(LocalDate.now().minusYears(59)), "50-59세")
318+
.otherwise("60대 이상").eq(ageGroup);
298319
}
299320

300321
}

src/main/kotlin/com/ddd/sonnypolabobe/domain/board/repository/vo/BoardGetOneVo.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ import java.util.UUID
66
data class BoardGetOneVo(
77
val id: UUID?,
88
val title: String?,
9+
val options: String?,
910
val ownerId: Long?,
1011
val polaroidId : Long?,
1112
val imageKey : String?,
1213
val oneLineMessage: String?,
1314
val createdAt: LocalDateTime?,
1415
val userId : Long?,
1516
val nickname: String?,
16-
val options: String?
17+
val polaroidOptions: String?
1718
)

0 commit comments

Comments
 (0)