Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion systems/notification/notification-adapter-in/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ kt_jvm_test(
javac_opts = "//:javac_options",
kotlinc_opts = "//:kotlinc_options",
test_class = "hs.kr.entrydsm.notification.adapterin.NotificationAdapterInModuleTest",
deps = MODULE_DEPS + TEST_DEPS,
deps = [":main"] + MODULE_DEPS + TEST_DEPS,
)
7 changes: 6 additions & 1 deletion systems/notification/notification-adapter-in/deps.bzl
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
KOTLIN_DEPS = []
# Dependencies for the notification REST adapter module.
KOTLIN_DEPS = [
"@maven//:org_springframework_boot_spring_boot_starter_web",
"//systems/notification/notification-application:main",
"//systems/notification/notification-domain:main",
]

TEST_DEPS = [
"@maven//:junit_junit",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package hs.kr.entrydsm.notification.adapterin.web

import hs.kr.entrydsm.notification.adapterin.web.dto.common.ApiResponse
import hs.kr.entrydsm.notification.adapterin.web.dto.common.toResponse
import hs.kr.entrydsm.notification.adapterin.web.dto.response.FaqDetailResponse
import hs.kr.entrydsm.notification.adapterin.web.dto.response.FaqSummaryResponse
import hs.kr.entrydsm.notification.adapterin.web.dto.response.NoticeDetailResponse
import hs.kr.entrydsm.notification.adapterin.web.dto.response.NoticeSummaryResponse
import hs.kr.entrydsm.notification.adapterin.web.dto.response.PageResponse
import hs.kr.entrydsm.notification.adapterin.web.dto.response.RecruitmentGuidelineResponse
import hs.kr.entrydsm.notification.application.port.`in`.NotificationPort
import hs.kr.entrydsm.notification.application.port.`in`.command.ReadFaqPageCommand
import hs.kr.entrydsm.notification.application.port.`in`.command.ReadNotificationPageCommand
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping("/api/notification/v11/notifications")
class NotificationController(
private val notificationPort: NotificationPort,
) {
@GetMapping("/notification")
fun getNotices(
@RequestParam(defaultValue = "0") page: Int,
@RequestParam(defaultValue = "10") size: Int,
@RequestParam(required = false) category: String?,
): ApiResponse<PageResponse<NoticeSummaryResponse>> {
val result = notificationPort.getNotices(
ReadNotificationPageCommand.of(
page = page,
size = size,
category = category,
),
)
return ApiResponse(
status = 200,
message = "공지 목록 조회 성공",
data = result.toResponse { it.toResponse() },
)
}

@GetMapping("/notification/{id}")
fun getNotice(
@PathVariable id: Long,
): ApiResponse<NoticeDetailResponse> {
val result = notificationPort.getNotice(id)
return ApiResponse(
status = 200,
message = "공지 상세 조회 성공",
data = result.toResponse(),
)
}

@GetMapping("/qna")
fun getFaqs(
@RequestParam(defaultValue = "0") page: Int,
@RequestParam(defaultValue = "10") size: Int,
@RequestParam(required = false) category: String?,
): ApiResponse<PageResponse<FaqSummaryResponse>> {
val result = notificationPort.getFaqs(
ReadFaqPageCommand.of(
page = page,
size = size,
category = category,
),
)
return ApiResponse(
status = 200,
message = "자주 묻는 질문 목록 조회 성공",
data = result.toResponse { it.toResponse() },
)
}

@GetMapping("/qna/{id}")
fun getFaq(
@PathVariable id: Long,
): ApiResponse<FaqDetailResponse> {
val result = notificationPort.getFaq(id)
return ApiResponse(
status = 200,
message = "자주 묻는 질문 상세 조회 성공",
data = result.toResponse(),
)
}

@GetMapping("/guideline")
fun getRecruitmentGuideline(): ApiResponse<RecruitmentGuidelineResponse> {
val result = notificationPort.getRecruitmentGuideline()
return ApiResponse(
status = 200,
message = "전형 요강 상세 조회 성공",
data = result.toResponse(),
)
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package hs.kr.entrydsm.notification.adapterin.web.dto.common

data class ApiResponse<T>(
val status: Int,
val message: String,
val data: T?,
)

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package hs.kr.entrydsm.notification.adapterin.web.dto.common

data class ErrorResponse(
val status: Int,
val message: String,
val code: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package hs.kr.entrydsm.notification.adapterin.web.dto.common

import hs.kr.entrydsm.notification.adapterin.web.dto.response.FaqDetailResponse
import hs.kr.entrydsm.notification.adapterin.web.dto.response.FaqSummaryResponse
import hs.kr.entrydsm.notification.adapterin.web.dto.response.NoticeDetailResponse
import hs.kr.entrydsm.notification.adapterin.web.dto.response.NoticeSummaryResponse
import hs.kr.entrydsm.notification.adapterin.web.dto.response.PageResponse
import hs.kr.entrydsm.notification.adapterin.web.dto.response.RecruitmentGuidelineResponse
import hs.kr.entrydsm.notification.adapterin.web.dto.response.RecruitmentScheduleResponse
import hs.kr.entrydsm.notification.application.port.`in`.result.FaqDetailResult
import hs.kr.entrydsm.notification.application.port.`in`.result.FaqSummaryResult
import hs.kr.entrydsm.notification.application.port.`in`.result.NoticeDetailResult
import hs.kr.entrydsm.notification.application.port.`in`.result.NoticeSummaryResult
import hs.kr.entrydsm.notification.application.port.`in`.result.PageResult
import hs.kr.entrydsm.notification.application.port.`in`.result.RecruitmentGuidelineResult

fun NoticeSummaryResult.toResponse(): NoticeSummaryResponse =
NoticeSummaryResponse(
noticeId = noticeId,
title = title,
author = author,
createdAt = createdAt,
)

fun NoticeDetailResult.toResponse(): NoticeDetailResponse =
NoticeDetailResponse(
noticeId = noticeId,
title = title,
content = content,
author = author,
viewCount = viewCount,
createdAt = createdAt,
updatedAt = updatedAt,
)

fun FaqSummaryResult.toResponse(): FaqSummaryResponse =
FaqSummaryResponse(
faqId = faqId,
category = category,
question = question,
answer = answer,
)

fun FaqDetailResult.toResponse(): FaqDetailResponse =
FaqDetailResponse(
faqId = faqId,
category = category,
question = question,
answer = answer,
viewCount = viewCount,
createdAt = createdAt,
updatedAt = updatedAt,
)

fun RecruitmentGuidelineResult.toResponse(): RecruitmentGuidelineResponse =
RecruitmentGuidelineResponse(
recruitmentId = recruitmentId,
title = title,
description = description,
schedule = RecruitmentScheduleResponse(
applicationStart = schedule.applicationStart,
applicationEnd = schedule.applicationEnd,
resultAt = schedule.resultAt,
),
createdAt = createdAt,
updatedAt = updatedAt,
)

fun <T, R> PageResult<T>.toResponse(mapper: (T) -> R): PageResponse<R> =
PageResponse(
content = content.map(mapper),
page = page,
size = size,
totalElements = totalElements,
totalPages = totalPages,
last = last,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package hs.kr.entrydsm.notification.adapterin.web.dto.response

import java.time.LocalDateTime

data class FaqSummaryResponse(
val faqId: Long,
val category: String,
val question: String,
val answer: String,
)

data class FaqDetailResponse(
val faqId: Long,
val category: String,
val question: String,
val answer: String,
val viewCount: Int,
val createdAt: LocalDateTime,
val updatedAt: LocalDateTime,
)

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package hs.kr.entrydsm.notification.adapterin.web.dto.response

import java.time.LocalDateTime

data class NoticeSummaryResponse(
val noticeId: Long,
val title: String,
val author: String,
val createdAt: LocalDateTime,
)

data class NoticeDetailResponse(
val noticeId: Long,
val title: String,
val content: String,
val author: String,
val viewCount: Int,
val createdAt: LocalDateTime,
val updatedAt: LocalDateTime,
)

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package hs.kr.entrydsm.notification.adapterin.web.dto.response

data class PageResponse<T>(
val content: List<T>,
val page: Int,
val size: Int,
val totalElements: Long,
val totalPages: Int,
val last: Boolean,
)

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package hs.kr.entrydsm.notification.adapterin.web.dto.response

import java.time.LocalDate
import java.time.LocalDateTime

data class RecruitmentGuidelineResponse(
val recruitmentId: Long,
val title: String,
val description: String,
val schedule: RecruitmentScheduleResponse,
val createdAt: LocalDateTime,
val updatedAt: LocalDateTime,
)

data class RecruitmentScheduleResponse(
val applicationStart: LocalDate,
val applicationEnd: LocalDate,
val resultAt: LocalDate,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package hs.kr.entrydsm.notification.adapterin.web.exception

import hs.kr.entrydsm.notification.adapterin.web.dto.common.ErrorResponse
import hs.kr.entrydsm.notification.application.exception.NotificationNotFoundException
import org.slf4j.LoggerFactory
import org.slf4j.MDC
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.http.converter.HttpMessageNotReadableException
import org.springframework.web.bind.MissingPathVariableException
import org.springframework.web.bind.MissingServletRequestParameterException
import org.springframework.web.bind.MethodArgumentNotValidException
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.RestControllerAdvice
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException

@RestControllerAdvice
class GlobalExceptionHandler {
private val logger = LoggerFactory.getLogger(javaClass)

@ExceptionHandler(NotificationNotFoundException::class)
fun handleNotFound(exception: NotificationNotFoundException): ResponseEntity<ErrorResponse> =
response(
status = HttpStatus.NOT_FOUND,
code = "NOTIFICATION_NOT_FOUND",
message = "notification not found",
)

@ExceptionHandler(
IllegalArgumentException::class,
HttpMessageNotReadableException::class,
MethodArgumentNotValidException::class,
MissingPathVariableException::class,
MissingServletRequestParameterException::class,
MethodArgumentTypeMismatchException::class,
)
fun handleInvalidRequest(exception: Exception): ResponseEntity<ErrorResponse> =
response(
status = HttpStatus.BAD_REQUEST,
code = "INVALID_REQUEST",
message = "invalid request",
)

@ExceptionHandler(Exception::class)
fun handleUnhandledException(exception: Exception): ResponseEntity<ErrorResponse> =
response(
status = HttpStatus.INTERNAL_SERVER_ERROR,
code = "INTERNAL_SERVER_ERROR",
message = "internal server error",
).also {
logger.error(
"Unhandled exception [correlationId={}]",
MDC.get("correlationId") ?: "unknown",
exception,
)
}

private fun response(
status: HttpStatus,
code: String,
message: String,
): ResponseEntity<ErrorResponse> =
ResponseEntity
.status(status)
.body(
ErrorResponse(
status = status.value(),
message = message,
code = code,
),
)
}
Loading
Loading