From fc36d7776650d8b4e2128da3d896bf05aeff72aa Mon Sep 17 00:00:00 2001 From: dldmsql Date: Wed, 30 Oct 2024 22:12:06 +0900 Subject: [PATCH] =?UTF-8?q?feat:=205=EC=B0=A8=20MVP=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EA=B0=9C=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 15 +-- .../board/controller/BoardController.kt | 13 +- .../controller/dto/BoardCreateRequest.kt | 6 +- .../board/controller/dto/BoardGetResponse.kt | 5 +- .../board/my/controller/MyBoardController.kt | 3 + .../repository/BoardJooqRepositoryImpl.kt | 111 +++++++++++------- .../board/repository/vo/BoardGetOneVo.kt | 3 +- .../domain/board/service/BoardService.kt | 32 ++++- .../domain/file/controller/FileController.kt | 2 + .../oauth/controller/OauthController.kt | 2 + .../controller/BoardPolaroidV2Controller.kt | 1 - .../polaroid/dto/PolaroidCreateRequest.kt | 4 +- .../polaroid/dto/PolaroidGetResponse.kt | 5 +- .../{PolaroidOption.kt => ExtraOption.kt} | 2 +- .../polaroid/service/PolaroidService.kt | 4 +- .../domain/user/controller/UserController.kt | 2 + .../user/repository/UserJooqRepositoryImpl.kt | 1 + .../domain/user/service/UserService.kt | 5 + .../global/HealthCheckController.kt | 2 + .../global/config/SwaggerConfig.kt | 9 +- 20 files changed, 145 insertions(+), 82 deletions(-) rename src/main/kotlin/com/ddd/sonnypolabobe/domain/polaroid/enumerate/{PolaroidOption.kt => ExtraOption.kt} (69%) diff --git a/build.gradle.kts b/build.gradle.kts index 2058318..9fdd6f0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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" } @@ -23,7 +21,7 @@ java { } jooq { - version.set("3.18.10") + version.set("3.19.0") edition.set(JooqEdition.OSS) configurations { @@ -59,12 +57,6 @@ jooq { } } -//allOpen { -// annotation("jakarta.persistence.Entity") -// annotation("jakarta.persistence.MappedSuperclass") -// annotation("jakarta.persistence.Embeddable") -//} - repositories { mavenCentral() } @@ -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") diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/controller/BoardController.kt b/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/controller/BoardController.kt index 06bc890..bbf05bc 100644 --- a/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/controller/BoardController.kt +++ b/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/controller/BoardController.kt @@ -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 @@ -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 필드 수정했습니다. 옵션이 추가되었습니다. """ ) @@ -51,6 +50,7 @@ class BoardController( return ApplicationResponse.ok(this.boardService.getById(id, user)) } + @Tag(name = "1.0.0") @Operation( summary = "보드 누적 생성 수 조회", description = """ 보드 누적 생성 수를 조회합니다. @@ -59,6 +59,7 @@ class BoardController( @GetMapping("/total-count") fun getTotalCount() = ApplicationResponse.ok(this.boardService.getTotalCount()) + @Tag(name = "1.0.0") @Operation( summary = "오늘 생성 가능한 보드 수 조회", description = """ 오늘 생성 가능한 보드 수를 조회합니다. diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/controller/dto/BoardCreateRequest.kt b/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/controller/dto/BoardCreateRequest.kt index fdb3cf6..50e6d98 100644 --- a/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/controller/dto/BoardCreateRequest.kt +++ b/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/controller/dto/BoardCreateRequest.kt @@ -1,9 +1,9 @@ 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 = "쏘니의 보드") @@ -11,5 +11,7 @@ data class BoardCreateRequest( @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 ) diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/controller/dto/BoardGetResponse.kt b/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/controller/dto/BoardGetResponse.kt index 000fd5f..f28092f 100644 --- a/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/controller/dto/BoardGetResponse.kt +++ b/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/controller/dto/BoardGetResponse.kt @@ -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( @@ -9,5 +10,7 @@ data class BoardGetResponse( @field:Schema(description = "폴라로이드") val items: List, @field:Schema(description = "작성자 여부", example = "true") - val isMine : Boolean + val isMine : Boolean, + @field:Schema(description = "옵션", example = "{\"THEMA\":\"value3\"}") + val options : Map? ) \ No newline at end of file diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/my/controller/MyBoardController.kt b/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/my/controller/MyBoardController.kt index b7fd8ce..9db6037 100644 --- a/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/my/controller/MyBoardController.kt +++ b/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/my/controller/MyBoardController.kt @@ -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 = """ 내 보드 목록을 조회합니다. @@ -30,6 +31,7 @@ class MyBoardController(private val myBoardService: MyBoardService) { } + @Tag(name = "1.0.0") @Operation( summary = "내 보드 이름 수정", description = """ @@ -47,6 +49,7 @@ class MyBoardController(private val myBoardService: MyBoardService) { return ApplicationResponse.ok() } + @Tag(name = "1.1.0") @Operation( summary = "내 보드 삭제", description = """ diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/repository/BoardJooqRepositoryImpl.kt b/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/repository/BoardJooqRepositoryImpl.kt index 130129a..81b9d6c 100644 --- a/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/repository/BoardJooqRepositoryImpl.kt +++ b/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/repository/BoardJooqRepositoryImpl.kt @@ -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 @@ -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 @@ -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) @@ -51,8 +51,9 @@ 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, @@ -60,7 +61,7 @@ class BoardJooqRepositoryImpl( jPolaroid.CREATED_AT, jPolaroid.USER_ID, jPolaroid.NICKNAME, - jPolaroid.OPTIONS + jPolaroid.OPTIONS.`as`(BoardGetOneVo::polaroidOptions.name) ) .from(jBoard) .leftJoin(jPolaroid).on( @@ -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 } @@ -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, @@ -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 { @@ -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 } @@ -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) { @@ -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); } } \ No newline at end of file diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/repository/vo/BoardGetOneVo.kt b/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/repository/vo/BoardGetOneVo.kt index b414708..7e115a3 100644 --- a/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/repository/vo/BoardGetOneVo.kt +++ b/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/repository/vo/BoardGetOneVo.kt @@ -6,6 +6,7 @@ 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?, @@ -13,5 +14,5 @@ data class BoardGetOneVo( val createdAt: LocalDateTime?, val userId : Long?, val nickname: String?, - val options: String? + val polaroidOptions: String? ) diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/service/BoardService.kt b/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/service/BoardService.kt index 718dcc6..7d89403 100644 --- a/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/service/BoardService.kt +++ b/src/main/kotlin/com/ddd/sonnypolabobe/domain/board/service/BoardService.kt @@ -4,7 +4,7 @@ import com.ddd.sonnypolabobe.domain.board.controller.dto.BoardCreateRequest import com.ddd.sonnypolabobe.domain.board.controller.dto.BoardGetResponse import com.ddd.sonnypolabobe.domain.board.repository.BoardJooqRepository import com.ddd.sonnypolabobe.domain.polaroid.dto.PolaroidGetResponse -import com.ddd.sonnypolabobe.domain.polaroid.enumerate.PolaroidOption +import com.ddd.sonnypolabobe.domain.polaroid.enumerate.ExtraOption import com.ddd.sonnypolabobe.domain.user.dto.UserDto import com.ddd.sonnypolabobe.global.exception.ApplicationException import com.ddd.sonnypolabobe.global.exception.CustomErrorCode @@ -15,6 +15,7 @@ import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.databind.ObjectMapper import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional import java.util.* @Service @@ -24,16 +25,21 @@ class BoardService( @Value("\${limit.count}") private val limit: Int ) { + @Transactional fun create(request: BoardCreateRequest): UUID = this.boardJooqRepository.insertOne(request)?.let { UuidConverter.byteArrayToUUID(it) } ?: throw ApplicationException(CustomErrorCode.BOARD_CREATED_FAILED) + @Transactional(readOnly = true) fun getById(id: String, user: AuthenticatedMember?): List { return id.run { val queryResult = boardJooqRepository.selectOneById(UuidConverter.stringToUUID(this@run)) - if(queryResult.isEmpty()) throw ApplicationException(CustomErrorCode.BOARD_NOT_FOUND) + + if (queryResult.isEmpty()) throw ApplicationException(CustomErrorCode.BOARD_NOT_FOUND) + val groupByTitle = queryResult.groupBy { it.title } + groupByTitle.map { entry -> val title = entry.key val polaroids = entry.value.map { @@ -45,22 +51,40 @@ class BoardService( nickname = it.nickname ?: "", isMine = it.userId == user?.id?.toLong(), createdAt = it.createdAt, - options = it.options?.let{ ObjectMapper().readValue(it, object : TypeReference>() {})} + options = it.polaroidOptions?.let { + ObjectMapper().readValue( + it, + object : TypeReference>() {}) + } ) }.filter { it.id != 0L }.distinctBy { it.id } - BoardGetResponse(title = title ?: "", items = polaroids, isMine = queryResult.first().ownerId == user?.id?.toLong()) + + BoardGetResponse( + title = title ?: "", + items = polaroids, + isMine = queryResult.first().ownerId == user?.id?.toLong(), + options = queryResult.first().options?.let { + ObjectMapper().readValue( + it, + object : TypeReference>() {}) + } + ) } } } + @Transactional(readOnly = true) fun getTotalCount(): Long = this.boardJooqRepository.selectTotalCount() + + @Transactional(readOnly = true) fun createAvailable(): Long { return this.boardJooqRepository.selectTodayTotalCount().let { if (it > limit) 0 else limit - it } } + @Transactional(readOnly = true) fun recommendTitle(user: UserDto.Companion.Res): List { val userBirthYear = user.birthDt val userGender = user.gender diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/domain/file/controller/FileController.kt b/src/main/kotlin/com/ddd/sonnypolabobe/domain/file/controller/FileController.kt index 7ac2b1c..c6e1b14 100644 --- a/src/main/kotlin/com/ddd/sonnypolabobe/domain/file/controller/FileController.kt +++ b/src/main/kotlin/com/ddd/sonnypolabobe/domain/file/controller/FileController.kt @@ -5,12 +5,14 @@ import com.ddd.sonnypolabobe.global.response.ApplicationResponse import com.ddd.sonnypolabobe.global.util.S3Util import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.media.Schema +import io.swagger.v3.oas.annotations.tags.Tag import org.springframework.web.bind.annotation.* import java.io.File import java.net.URL import java.util.* +@Tag(name = "1.0.0") @RestController @RequestMapping("/api/v1/file") class FileController( diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/domain/oauth/controller/OauthController.kt b/src/main/kotlin/com/ddd/sonnypolabobe/domain/oauth/controller/OauthController.kt index 4275be1..b270d41 100644 --- a/src/main/kotlin/com/ddd/sonnypolabobe/domain/oauth/controller/OauthController.kt +++ b/src/main/kotlin/com/ddd/sonnypolabobe/domain/oauth/controller/OauthController.kt @@ -4,10 +4,12 @@ import com.ddd.sonnypolabobe.domain.oauth.service.OauthService 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.* +@Tag(name = "1.0.0") @RestController @RequestMapping("/api/v1/oauth") class OauthController(private val oauthService: OauthService) { diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/domain/polaroid/controller/BoardPolaroidV2Controller.kt b/src/main/kotlin/com/ddd/sonnypolabobe/domain/polaroid/controller/BoardPolaroidV2Controller.kt index d2a66c4..01638a8 100644 --- a/src/main/kotlin/com/ddd/sonnypolabobe/domain/polaroid/controller/BoardPolaroidV2Controller.kt +++ b/src/main/kotlin/com/ddd/sonnypolabobe/domain/polaroid/controller/BoardPolaroidV2Controller.kt @@ -1,7 +1,6 @@ package com.ddd.sonnypolabobe.domain.polaroid.controller import com.ddd.sonnypolabobe.domain.polaroid.dto.PolaroidCreateRequest -import com.ddd.sonnypolabobe.domain.polaroid.enumerate.PolaroidOption import com.ddd.sonnypolabobe.domain.polaroid.service.PolaroidService import com.ddd.sonnypolabobe.global.response.ApplicationResponse import com.ddd.sonnypolabobe.global.security.JwtUtil diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/domain/polaroid/dto/PolaroidCreateRequest.kt b/src/main/kotlin/com/ddd/sonnypolabobe/domain/polaroid/dto/PolaroidCreateRequest.kt index 03bea72..c19c33b 100644 --- a/src/main/kotlin/com/ddd/sonnypolabobe/domain/polaroid/dto/PolaroidCreateRequest.kt +++ b/src/main/kotlin/com/ddd/sonnypolabobe/domain/polaroid/dto/PolaroidCreateRequest.kt @@ -1,6 +1,6 @@ package com.ddd.sonnypolabobe.domain.polaroid.dto -import com.ddd.sonnypolabobe.domain.polaroid.enumerate.PolaroidOption +import com.ddd.sonnypolabobe.domain.polaroid.enumerate.ExtraOption import io.swagger.v3.oas.annotations.media.Schema import jakarta.validation.constraints.Size @@ -14,5 +14,5 @@ data class PolaroidCreateRequest( @field:Schema(description = "작성자 닉네임이 null 이면서 회원가입된 유저라면, 유저의 닉네임을 자동할당합니다.", example = "작성자 닉네임") var nickname : String?, @field:Schema(description = "폴라로이드 옵션", example = "{\"FONT\":\"value1\",\"FILTER\":\"value2\",\"THEMA\":\"value3\"}") - val options : Map + val options : Map ) \ No newline at end of file diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/domain/polaroid/dto/PolaroidGetResponse.kt b/src/main/kotlin/com/ddd/sonnypolabobe/domain/polaroid/dto/PolaroidGetResponse.kt index 80e8901..f16bda0 100644 --- a/src/main/kotlin/com/ddd/sonnypolabobe/domain/polaroid/dto/PolaroidGetResponse.kt +++ b/src/main/kotlin/com/ddd/sonnypolabobe/domain/polaroid/dto/PolaroidGetResponse.kt @@ -1,9 +1,8 @@ package com.ddd.sonnypolabobe.domain.polaroid.dto -import com.ddd.sonnypolabobe.domain.polaroid.enumerate.PolaroidOption +import com.ddd.sonnypolabobe.domain.polaroid.enumerate.ExtraOption import io.swagger.v3.oas.annotations.media.Schema import java.time.LocalDateTime -import java.util.UUID data class PolaroidGetResponse( @field:Schema(description = "폴라로이드 ID", example = "1") @@ -21,5 +20,5 @@ data class PolaroidGetResponse( @field:Schema(description = "생성일시", example = "2021-01-01T00:00:00") val createdAt: LocalDateTime?, @field:Schema(description = "옵션") - val options: Map? + val options: Map? ) diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/domain/polaroid/enumerate/PolaroidOption.kt b/src/main/kotlin/com/ddd/sonnypolabobe/domain/polaroid/enumerate/ExtraOption.kt similarity index 69% rename from src/main/kotlin/com/ddd/sonnypolabobe/domain/polaroid/enumerate/PolaroidOption.kt rename to src/main/kotlin/com/ddd/sonnypolabobe/domain/polaroid/enumerate/ExtraOption.kt index 84d18be..789602c 100644 --- a/src/main/kotlin/com/ddd/sonnypolabobe/domain/polaroid/enumerate/PolaroidOption.kt +++ b/src/main/kotlin/com/ddd/sonnypolabobe/domain/polaroid/enumerate/ExtraOption.kt @@ -1,6 +1,6 @@ package com.ddd.sonnypolabobe.domain.polaroid.enumerate -enum class PolaroidOption(val description: String) { +enum class ExtraOption(val description: String) { FONT("폰트"), FILTER("필터"), THEMA("테마") diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/domain/polaroid/service/PolaroidService.kt b/src/main/kotlin/com/ddd/sonnypolabobe/domain/polaroid/service/PolaroidService.kt index 49677fd..2f3a70a 100644 --- a/src/main/kotlin/com/ddd/sonnypolabobe/domain/polaroid/service/PolaroidService.kt +++ b/src/main/kotlin/com/ddd/sonnypolabobe/domain/polaroid/service/PolaroidService.kt @@ -3,7 +3,7 @@ package com.ddd.sonnypolabobe.domain.polaroid.service import com.ddd.sonnypolabobe.domain.board.repository.BoardJooqRepository import com.ddd.sonnypolabobe.domain.polaroid.dto.PolaroidCreateRequest import com.ddd.sonnypolabobe.domain.polaroid.dto.PolaroidGetResponse -import com.ddd.sonnypolabobe.domain.polaroid.enumerate.PolaroidOption +import com.ddd.sonnypolabobe.domain.polaroid.enumerate.ExtraOption import com.ddd.sonnypolabobe.domain.polaroid.repository.PolaroidJooqRepository import com.ddd.sonnypolabobe.domain.user.dto.UserDto import com.ddd.sonnypolabobe.global.exception.ApplicationException @@ -48,7 +48,7 @@ class PolaroidService( nickname = it.nickname ?: "", isMine = boardWriter.userId == user.id, createdAt = it.createdAt, - options = it.options?.let{ ObjectMapper().readValue(it, object : TypeReference>() {})} + options = it.options?.let{ ObjectMapper().readValue(it, object : TypeReference>() {})} ) } } diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/domain/user/controller/UserController.kt b/src/main/kotlin/com/ddd/sonnypolabobe/domain/user/controller/UserController.kt index be0b860..43a7063 100644 --- a/src/main/kotlin/com/ddd/sonnypolabobe/domain/user/controller/UserController.kt +++ b/src/main/kotlin/com/ddd/sonnypolabobe/domain/user/controller/UserController.kt @@ -4,9 +4,11 @@ import com.ddd.sonnypolabobe.domain.user.dto.UserDto import com.ddd.sonnypolabobe.domain.user.service.UserService import com.ddd.sonnypolabobe.global.response.ApplicationResponse import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.tags.Tag import org.springframework.security.core.context.SecurityContextHolder import org.springframework.web.bind.annotation.* +@Tag(name = "1.0.0") @RestController @RequestMapping("/api/v1/user") class UserController( diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/domain/user/repository/UserJooqRepositoryImpl.kt b/src/main/kotlin/com/ddd/sonnypolabobe/domain/user/repository/UserJooqRepositoryImpl.kt index 91816a9..d5829d6 100644 --- a/src/main/kotlin/com/ddd/sonnypolabobe/domain/user/repository/UserJooqRepositoryImpl.kt +++ b/src/main/kotlin/com/ddd/sonnypolabobe/domain/user/repository/UserJooqRepositoryImpl.kt @@ -88,6 +88,7 @@ class UserJooqRepositoryImpl(private val dslContext: DSLContext) : UserJooqRepos val jUser = User.USER this.dslContext.update(jUser) .set(jUser.YN, 0) + .set(jUser.DELETED_AT, DateConverter.convertToKst(LocalDateTime.now())) .where(jUser.ID.eq(id)) .execute() } diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/domain/user/service/UserService.kt b/src/main/kotlin/com/ddd/sonnypolabobe/domain/user/service/UserService.kt index 17fe370..96fef6a 100644 --- a/src/main/kotlin/com/ddd/sonnypolabobe/domain/user/service/UserService.kt +++ b/src/main/kotlin/com/ddd/sonnypolabobe/domain/user/service/UserService.kt @@ -4,16 +4,19 @@ import com.ddd.sonnypolabobe.domain.user.dto.UserDto import com.ddd.sonnypolabobe.domain.user.repository.UserJooqRepository import com.ddd.sonnypolabobe.domain.user.withdraw.repository.WithdrawJooqRepository import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional @Service class UserService( private val userJooqRepository: UserJooqRepository, private val withdrawJooqRepository: WithdrawJooqRepository ) { + @Transactional fun updateProfile(request: UserDto.Companion.UpdateReq, userId: Long) { this.userJooqRepository.updateProfile(request, userId) } + @Transactional(readOnly = true) fun findById(id: Long): UserDto.Companion.ProfileRes { return this.userJooqRepository.findById(id).let { UserDto.Companion.ProfileRes( @@ -25,11 +28,13 @@ class UserService( } } + @Transactional fun withdraw(request: UserDto.Companion.WithdrawReq, id: Long) { this.withdrawJooqRepository.insertOne(request, id) this.userJooqRepository.deleteById(id) } + @Transactional(readOnly = true) fun checkExist(email: String): Boolean { return this.userJooqRepository.findByEmail(email) != null } diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/global/HealthCheckController.kt b/src/main/kotlin/com/ddd/sonnypolabobe/global/HealthCheckController.kt index 3bb0061..3b97b4c 100644 --- a/src/main/kotlin/com/ddd/sonnypolabobe/global/HealthCheckController.kt +++ b/src/main/kotlin/com/ddd/sonnypolabobe/global/HealthCheckController.kt @@ -1,8 +1,10 @@ package com.ddd.sonnypolabobe.global +import io.swagger.v3.oas.annotations.tags.Tag import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RestController +@Tag(name = "1.0.0") @RestController class HealthCheckController { diff --git a/src/main/kotlin/com/ddd/sonnypolabobe/global/config/SwaggerConfig.kt b/src/main/kotlin/com/ddd/sonnypolabobe/global/config/SwaggerConfig.kt index 627e706..dad5d01 100644 --- a/src/main/kotlin/com/ddd/sonnypolabobe/global/config/SwaggerConfig.kt +++ b/src/main/kotlin/com/ddd/sonnypolabobe/global/config/SwaggerConfig.kt @@ -2,6 +2,7 @@ package com.ddd.sonnypolabobe.global.config import io.swagger.v3.oas.models.Components import io.swagger.v3.oas.models.OpenAPI +import io.swagger.v3.oas.models.info.Info import io.swagger.v3.oas.models.security.SecurityRequirement import io.swagger.v3.oas.models.security.SecurityScheme import io.swagger.v3.oas.models.servers.Server @@ -21,11 +22,15 @@ class SwaggerConfig { .addServersItem(Server().url("/")) .components(Components().addSecuritySchemes("bearerAuth", securityScheme)) .security(listOf(securityRequirement)) + .info(Info().version("1.4.0").description("5차 MVP")) } private fun getSecurityScheme(): SecurityScheme = - SecurityScheme().type(SecurityScheme.Type.HTTP).scheme("Bearer").bearerFormat("JWT") - .`in`(SecurityScheme.In.HEADER).name("Authorization") + SecurityScheme() + .type(SecurityScheme.Type.HTTP) + .scheme("Bearer").bearerFormat("JWT") + .`in`(SecurityScheme.In.HEADER) + .name("Authorization") private fun getSecurityRequirement(): SecurityRequirement =