Skip to content

Commit

Permalink
Merge pull request #39 from DDD-Community/dev
Browse files Browse the repository at this point in the history
4차 mvp 릴리즈
  • Loading branch information
dldmsql authored Sep 28, 2024
2 parents 69b6f28 + b6f4384 commit 481e7f7
Show file tree
Hide file tree
Showing 51 changed files with 479 additions and 520 deletions.
48 changes: 24 additions & 24 deletions .github/workflows/healthCheck.yml
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
name: "[운영] 헬스체크"

on:
schedule:
- cron: "0 0 */3 * *"

jobs:
healthcheck:
runs-on: ubuntu-latest
steps:
- name: API Health Check
id: health_check
uses: jtalk/url-health-check-action@v3
with:
url: ${{ secrets.BASE_URI_PROD }}
max-attempts: 3
retry-delay: 1s

- name: Discord Webhook Action
if: always()
uses: tsickert/[email protected]
with:
webhook-url: ${{ secrets.WEBHOOK_URL }}
content: ${{ job.status }}
#name: "[운영] 헬스체크"
#
#on:
# schedule:
# - cron: "0 0 */3 * *"
#
#jobs:
# healthcheck:
# runs-on: ubuntu-latest
# steps:
# - name: API Health Check
# id: health_check
# uses: jtalk/url-health-check-action@v3
# with:
# url: ${{ secrets.BASE_URI_PROD }}
# max-attempts: 3
# retry-delay: 1s
#
# - name: Discord Webhook Action
# if: always()
# uses: tsickert/[email protected]
# with:
# webhook-url: ${{ secrets.WEBHOOK_URL }}
# content: ${{ job.status }}
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,19 @@ import com.ddd.sonnypolabobe.domain.board.controller.dto.BoardGetResponse
import com.ddd.sonnypolabobe.domain.board.service.BoardService
import com.ddd.sonnypolabobe.domain.user.dto.UserDto
import com.ddd.sonnypolabobe.global.response.ApplicationResponse
import com.ddd.sonnypolabobe.logger
import com.ddd.sonnypolabobe.global.security.JwtUtil
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.GetMapping
import org.springframework.web.bind.annotation.PathVariable
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
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 boardService: BoardService,
private val jwtUtil: JwtUtil
) {
@Operation(
summary = "보드 생성", description = """
Expand All @@ -32,23 +28,28 @@ class BoardController(
"""
)
@PostMapping
fun create(@RequestBody request: BoardCreateRequest)
: ApplicationResponse<UUID> {
fun create(@RequestBody request: BoardCreateRequest) : ApplicationResponse<UUID> {
val user =
SecurityContextHolder.getContext().authentication.principal as UserDto.Companion.Res
request.userId = user.id
return ApplicationResponse.ok(this.boardService.create(request))
}

@Tag(name = "1.1.0")
@Tag(name = "1.3.0")
@Operation(
summary = "보드 조회", description = """
보드를 조회합니다.
DTO 필드 수정했습니다. 폴라로이드에 닉네임 필드 추가
DTO 필드 수정했습니다. 스티커 리스트 추가했습니다.
"""
)
@GetMapping("/{id}")
fun get(@PathVariable id: String) = ApplicationResponse.ok(this.boardService.getById(id))
fun get(@PathVariable id: String,
@RequestHeader("Authorization") token: String?
) : ApplicationResponse<List<BoardGetResponse>> {
val user = token?.let { this.jwtUtil.getAuthenticatedMemberFromToken(it) }
return ApplicationResponse.ok(this.boardService.getById(id, user))
}

@Operation(
summary = "보드 누적 생성 수 조회", description = """
Expand All @@ -65,4 +66,17 @@ class BoardController(
)
@GetMapping("/create-available")
fun createAvailable() = ApplicationResponse.ok(this.boardService.createAvailable())

@Tag(name = "1.2.0")
@Operation(
summary = "보드명 주제 추천", description = """
보드명 주제를 추천합니다.
"""
)
@GetMapping("/recommend-title")
fun recommendTitle() : ApplicationResponse<List<String>> {
val user =
SecurityContextHolder.getContext().authentication.principal as UserDto.Companion.Res
return ApplicationResponse.ok(this.boardService.recommendTitle(user))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import jakarta.validation.constraints.Pattern
import java.util.*

data class BoardCreateRequest(
@Schema(description = "제목", example = "쏘니의 보드")
@field:Schema(description = "제목", example = "쏘니의 보드")
@field:NotBlank
@field:Pattern(regexp = "^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*()_+=-])(?=.*[ㄱ-ㅎㅏ-ㅣ가-힣]).{1,20}$", message = "제목은 국문, 영문, 숫자, 특수문자, 띄어쓰기를 포함한 20자 이내여야 합니다.")
val title: String,
@Schema(description = "작성자 아이디", example = "null", required = false)
@field:Schema(description = "작성자 아이디", example = "null", required = false)
var userId: Long? = null
)
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.ddd.sonnypolabobe.domain.board.controller.dto

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

data class BoardGetResponse(
@Schema(description = "제목", example = "쏘니의 보드")
@field:Schema(description = "제목", example = "쏘니의 보드")
val title: String,
@Schema(description = "작성자", example = "작성자입니다.")
val items: List<PolaroidGetResponse>
@field:Schema(description = "폴라로이드")
val items: List<PolaroidGetResponse>,
@field:Schema(description = "작성자 여부", example = "true")
val isMine : Boolean
)

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.ddd.sonnypolabobe.domain.board.my.dto

import com.fasterxml.jackson.annotation.JsonProperty
import io.swagger.v3.oas.annotations.media.Schema
import org.springframework.format.annotation.DateTimeFormat
import java.time.LocalDateTime
import java.util.UUID
Expand All @@ -9,26 +10,36 @@ class MyBoardDto {
companion object {
data class MBUpdateReq(
@JsonProperty("title")
@field:Schema(description = "제목", example = "쏘니의 보드")
val title: String
)

data class PageListRes(
@field:Schema(description = "보드 아이디", example = "01906259-94b2-74ef-8c13-554385c42943")
val id: UUID,
@field:Schema(description = "제목", example = "쏘니의 보드")
val title: String,
@DateTimeFormat(pattern = "yyyy-MM-dd", iso = DateTimeFormat.ISO.DATE)
@field:Schema(description = "생성일", example = "2021-07-01")
val createdAt: LocalDateTime,
)

data class GetOneRes(
@field:Schema(description = "보드 아이디", example = "01906259-94b2-74ef-8c13-554385c42943")
val id: UUID,
@field:Schema(description = "제목", example = "쏘니의 보드")
val title: String,
@DateTimeFormat(pattern = "yyyy-MM-dd", iso = DateTimeFormat.ISO.DATE)
@field:Schema(description = "생성일", example = "2021-07-01")
val createdAt: LocalDateTime,
@field:Schema(description = "작성자 아이디", example = "null", required = false)
val userId: Long?
)

data class TotalCountRes(
@field:Schema(description = "총 보드 생성 수", example = "100")
val totalCreateCount: Long,
@field:Schema(description = "총 참여자 수", example = "1000")
val totalParticipantCount: Long
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ package com.ddd.sonnypolabobe.domain.board.repository

import com.ddd.sonnypolabobe.domain.board.controller.dto.BoardCreateRequest
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
import com.ddd.sonnypolabobe.jooq.polabo.tables.Board
import org.jooq.Record6
import org.jooq.Record7
import java.time.LocalDate
import java.time.LocalDateTime
import java.util.*

interface BoardJooqRepository {
fun insertOne(request: BoardCreateRequest): ByteArray?
fun selectOneById(id: UUID) : Array<out Record7<String?, Long?, String?, String?, LocalDateTime?, Long?, String?>>
fun selectOneById(id: UUID) : List<BoardGetOneVo>
fun selectTotalCount(): Long
fun selectTodayTotalCount(): Long
fun findById(id: UUID): MyBoardDto.Companion.GetOneRes?
Expand All @@ -25,4 +28,5 @@ interface BoardJooqRepository {
): List<MyBoardDto.Companion.PageListRes>

fun selectTotalCountByParticipant(userId: Long): Long
fun selectRecommendTitle(userBirth: LocalDate?, userGender: GenderType): List<String>
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,24 @@ 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
import com.ddd.sonnypolabobe.global.util.DateConverter
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 org.jooq.DSLContext
import org.jooq.Record6
import org.jooq.Record7
import com.ddd.sonnypolabobe.jooq.polabo.tables.User
import org.jooq.*
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
import java.util.*

@Repository
Expand All @@ -38,18 +45,22 @@ class BoardJooqRepositoryImpl(
return if (result == 1) id else null
}

override fun selectOneById(id: UUID): Array<out Record7<String?, Long?, String?, String?, LocalDateTime?, Long?, String?>> {
override fun selectOneById(id: UUID): List<BoardGetOneVo> {
val jBoard = Board.BOARD
val jPolaroid = Polaroid.POLAROID

return this.dslContext
.select(
jBoard.ID.convertFrom { it?.let{UuidConverter.byteArrayToUUID(it) } },
jBoard.TITLE,
jPolaroid.ID,
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.NICKNAME,
jPolaroid.OPTIONS
)
.from(jBoard)
.leftJoin(jPolaroid).on(
Expand All @@ -61,7 +72,7 @@ class BoardJooqRepositoryImpl(
.and(jBoard.ACTIVEYN.eq(1))
)
.orderBy(jPolaroid.CREATED_AT.desc())
.fetchArray()
.fetchInto(BoardGetOneVo::class.java)

}

Expand Down Expand Up @@ -216,4 +227,74 @@ class BoardJooqRepositoryImpl(
.fetchOne(0, Long::class.java)
?: 0L
}

override fun selectRecommendTitle(userBirth: LocalDate?, userGender: GenderType): List<String> {
val jBoard = Board.BOARD
val jUser = User.USER
val jPolaroid = Polaroid.POLAROID
// 현재 날짜 기준으로 연령대를 계산하는 로직
var userAgeGroup : String = "20-29세"
if (userBirth != null) {
val age = ChronoUnit.YEARS.between(userBirth, LocalDate.now())
userAgeGroup = if (age < 15) {
"15세 미만"
} else if (age < 20) {
"15-19세"
} else if (age < 30) {
"20-29세"
} else if (age < 40) {
"30-39세"
} else if (age < 50) {
"40-49세"
} else if (age < 60) {
"50-59세"
} else {
"60대 이상"
}
}

// 기준일 (30일 전)
val thirtyDaysAgo = LocalDateTime.now().minusDays(30)

// 쿼리 작성
return this.dslContext.select(jBoard.TITLE)
.from(jBoard)
.join(jUser)
.on(jBoard.USER_ID.eq(jUser.ID))
.leftJoin(
this.dslContext.select(jPolaroid.BOARD_ID, count().`as`("polaroid_count"))
.from(jPolaroid)
.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())
.limit(16)
.fetchInto(String::class.java)
}

// 성별 및 연령대 일치 조건을 위한 메서드
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{
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);
}

}
Loading

0 comments on commit 481e7f7

Please sign in to comment.