[#41] 예약 스키마 재정의 #42

Merged
pricelees merged 41 commits from refactor/#41 into main 2025-09-09 00:43:39 +00:00
24 changed files with 41 additions and 83 deletions
Showing only changes of commit 865026aff2 - Show all commits

View File

@ -42,7 +42,7 @@ abstract class PersistableBaseEntity(
@Transient @Transient
private var isNewEntity: Boolean = true private var isNewEntity: Boolean = true
): Persistable<Long> { ) : Persistable<Long> {
@PostLoad @PostLoad
@PostPersist @PostPersist
fun markNotNew() { fun markNotNew() {

View File

@ -18,22 +18,18 @@ abstract class AuditingBaseEntity(
@Column(updatable = false) @Column(updatable = false)
@CreatedDate @CreatedDate
lateinit var createdAt: LocalDateTime lateinit var createdAt: LocalDateTime
protected set
@Column(updatable = false) @Column(updatable = false)
@CreatedBy @CreatedBy
var createdBy: Long = 0L var createdBy: Long = 0L
protected set
@Column @Column
@LastModifiedDate @LastModifiedDate
lateinit var updatedAt: LocalDateTime lateinit var updatedAt: LocalDateTime
protected set
@Column @Column
@LastModifiedBy @LastModifiedBy
var updatedBy: Long = 0L var updatedBy: Long = 0L
protected set
} }
@MappedSuperclass @MappedSuperclass

View File

@ -57,7 +57,7 @@ class ControllerLoggingAspect(
) )
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
convertResponseMessageRequest = convertResponseMessageRequest.copy( convertResponseMessageRequest = convertResponseMessageRequest.copy(
body = responseEntity.body body = responseEntity.body
) )
} }

View File

@ -22,7 +22,7 @@ class MemberEntity(
@Column(name = "role", nullable = false, length = 20) @Column(name = "role", nullable = false, length = 20)
@Enumerated(value = EnumType.STRING) @Enumerated(value = EnumType.STRING)
var role: Role var role: Role
): BaseEntity() { ) : BaseEntity() {
override fun getId(): Long? = _id override fun getId(): Long? = _id
fun isAdmin(): Boolean = role == Role.ADMIN fun isAdmin(): Boolean = role == Role.ADMIN

View File

@ -10,19 +10,8 @@ import roomescape.payment.exception.PaymentException
import roomescape.payment.infrastructure.client.PaymentClientCancelResponse import roomescape.payment.infrastructure.client.PaymentClientCancelResponse
import roomescape.payment.infrastructure.client.PaymentClientConfirmResponse import roomescape.payment.infrastructure.client.PaymentClientConfirmResponse
import roomescape.payment.infrastructure.client.TosspayClient import roomescape.payment.infrastructure.client.TosspayClient
import roomescape.payment.infrastructure.persistence.CanceledPaymentEntity import roomescape.payment.infrastructure.persistence.*
import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository import roomescape.payment.web.*
import roomescape.payment.infrastructure.persistence.PaymentDetailEntity
import roomescape.payment.infrastructure.persistence.PaymentDetailRepository
import roomescape.payment.infrastructure.persistence.PaymentEntity
import roomescape.payment.infrastructure.persistence.PaymentRepository
import roomescape.payment.web.PaymentCancelRequest
import roomescape.payment.web.PaymentConfirmRequest
import roomescape.payment.web.PaymentCreateResponse
import roomescape.payment.web.PaymentRetrieveResponse
import roomescape.payment.web.toCancelDetailResponse
import roomescape.payment.web.toPaymentDetailResponse
import roomescape.payment.web.toRetrieveResponse
private val log: KLogger = KotlinLogging.logger {} private val log: KLogger = KotlinLogging.logger {}

View File

@ -10,12 +10,7 @@ import roomescape.payment.exception.PaymentException
import roomescape.payment.infrastructure.client.* import roomescape.payment.infrastructure.client.*
import roomescape.payment.infrastructure.common.PaymentMethod import roomescape.payment.infrastructure.common.PaymentMethod
import roomescape.payment.infrastructure.common.PaymentType import roomescape.payment.infrastructure.common.PaymentType
import roomescape.payment.infrastructure.persistence.CanceledPaymentEntity import roomescape.payment.infrastructure.persistence.*
import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository
import roomescape.payment.infrastructure.persistence.PaymentDetailEntity
import roomescape.payment.infrastructure.persistence.PaymentDetailRepository
import roomescape.payment.infrastructure.persistence.PaymentEntity
import roomescape.payment.infrastructure.persistence.PaymentRepository
import java.time.LocalDateTime import java.time.LocalDateTime
private val log: KLogger = KotlinLogging.logger {} private val log: KLogger = KotlinLogging.logger {}

View File

@ -61,7 +61,7 @@ private class ConfirmClient(
private val errorHandler: TosspayErrorHandler = TosspayErrorHandler(objectMapper) private val errorHandler: TosspayErrorHandler = TosspayErrorHandler(objectMapper)
fun request(paymentKey: String, orderId: String, amount: Int): PaymentClientConfirmResponse { fun request(paymentKey: String, orderId: String, amount: Int): PaymentClientConfirmResponse {
val response = client.post() val response = client.post()
.uri(CONFIRM_URI) .uri(CONFIRM_URI)
.contentType(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)

View File

@ -23,9 +23,9 @@ enum class PaymentType(
@JsonCreator(mode = JsonCreator.Mode.DELEGATING) @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
fun get(name: String): PaymentType { fun get(name: String): PaymentType {
return CACHE[name.uppercase()] ?: run { return CACHE[name.uppercase()] ?: run {
log.warn { "[PaymentTypes.PaymentType] 결제 타입 조회 실패: type=$name" } log.warn { "[PaymentTypes.PaymentType] 결제 타입 조회 실패: type=$name" }
throw PaymentException(PaymentErrorCode.TYPE_NOT_FOUND) throw PaymentException(PaymentErrorCode.TYPE_NOT_FOUND)
} }
} }
} }
} }
@ -163,9 +163,9 @@ enum class BankCode(
val parsedCode = if (code.length == 2) "0$code" else code val parsedCode = if (code.length == 2) "0$code" else code
return CACHE[parsedCode] ?: run { return CACHE[parsedCode] ?: run {
log.error { "[PaymentCode.BankCode] 은행 코드 조회 실패: code=$code" } log.error { "[PaymentCode.BankCode] 은행 코드 조회 실패: code=$code" }
throw PaymentException(PaymentErrorCode.ORGANIZATION_CODE_NOT_FOUND) throw PaymentException(PaymentErrorCode.ORGANIZATION_CODE_NOT_FOUND)
} }
} }
} }
} }
@ -207,9 +207,9 @@ enum class CardIssuerCode(
@JsonCreator(mode = JsonCreator.Mode.DELEGATING) @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
fun get(code: String): CardIssuerCode { fun get(code: String): CardIssuerCode {
return CACHE[code] ?: run { return CACHE[code] ?: run {
log.error { "[PaymentCode.CardIssuerCode] 카드사 코드 조회 실패: code=$code" } log.error { "[PaymentCode.CardIssuerCode] 카드사 코드 조회 실패: code=$code" }
throw PaymentException(PaymentErrorCode.ORGANIZATION_CODE_NOT_FOUND) throw PaymentException(PaymentErrorCode.ORGANIZATION_CODE_NOT_FOUND)
} }
} }
} }
} }

View File

@ -2,6 +2,6 @@ package roomescape.payment.infrastructure.persistence
import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.JpaRepository
interface PaymentDetailRepository: JpaRepository<PaymentDetailEntity, Long> { interface PaymentDetailRepository : JpaRepository<PaymentDetailEntity, Long> {
fun findByPaymentId(paymentId: Long) : PaymentDetailEntity? fun findByPaymentId(paymentId: Long): PaymentDetailEntity?
} }

View File

@ -5,9 +5,7 @@ import roomescape.payment.exception.PaymentException
import roomescape.payment.infrastructure.common.PaymentStatus import roomescape.payment.infrastructure.common.PaymentStatus
import roomescape.payment.infrastructure.common.PaymentType import roomescape.payment.infrastructure.common.PaymentType
import roomescape.payment.infrastructure.persistence.* import roomescape.payment.infrastructure.persistence.*
import roomescape.payment.web.PaymentDetailResponse.BankTransferDetailResponse import roomescape.payment.web.PaymentDetailResponse.*
import roomescape.payment.web.PaymentDetailResponse.CardDetailResponse
import roomescape.payment.web.PaymentDetailResponse.EasyPayPrepaidDetailResponse
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.OffsetDateTime import java.time.OffsetDateTime

View File

@ -11,11 +11,7 @@ import org.springframework.web.bind.annotation.RequestBody
import roomescape.auth.web.support.LoginRequired import roomescape.auth.web.support.LoginRequired
import roomescape.auth.web.support.MemberId import roomescape.auth.web.support.MemberId
import roomescape.common.dto.response.CommonApiResponse import roomescape.common.dto.response.CommonApiResponse
import roomescape.reservation.web.PendingReservationCreateRequest import roomescape.reservation.web.*
import roomescape.reservation.web.PendingReservationCreateResponse
import roomescape.reservation.web.ReservationCancelRequest
import roomescape.reservation.web.ReservationDetailRetrieveResponse
import roomescape.reservation.web.ReservationSummaryRetrieveListResponse
interface ReservationAPI { interface ReservationAPI {
@ -56,5 +52,4 @@ interface ReservationAPI {
fun findDetailById( fun findDetailById(
@PathVariable("id") id: Long @PathVariable("id") id: Long
): ResponseEntity<CommonApiResponse<ReservationDetailRetrieveResponse>> ): ResponseEntity<CommonApiResponse<ReservationDetailRetrieveResponse>>
} }

View File

@ -20,7 +20,7 @@ class CanceledReservationEntity(
@Enumerated(value = EnumType.STRING) @Enumerated(value = EnumType.STRING)
val status: CanceledReservationStatus, val status: CanceledReservationStatus,
): BaseEntityV2(id) ) : BaseEntityV2(id)
enum class CanceledReservationStatus { enum class CanceledReservationStatus {
PROCESSING, FAILED, COMPLETED PROCESSING, FAILED, COMPLETED

View File

@ -2,4 +2,4 @@ package roomescape.reservation.infrastructure.persistence
import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.JpaRepository
interface CanceledReservationRepository: JpaRepository<CanceledReservationEntity, Long> interface CanceledReservationRepository : JpaRepository<CanceledReservationEntity, Long>

View File

@ -20,8 +20,7 @@ class ReservationEntity(
@Enumerated(value = EnumType.STRING) @Enumerated(value = EnumType.STRING)
var status: ReservationStatus, var status: ReservationStatus,
) : AuditingBaseEntity(id) {
) : AuditingBaseEntity(id) {
fun confirm() { fun confirm() {
this.status = ReservationStatus.CONFIRMED this.status = ReservationStatus.CONFIRMED
} }

View File

@ -23,7 +23,7 @@ class ScheduleValidator(
fun validateCanDelete(schedule: ScheduleEntity) { fun validateCanDelete(schedule: ScheduleEntity) {
val status: ScheduleStatus = schedule.status val status: ScheduleStatus = schedule.status
if (status !in listOf(ScheduleStatus.AVAILABLE,ScheduleStatus.BLOCKED)) { if (status !in listOf(ScheduleStatus.AVAILABLE, ScheduleStatus.BLOCKED)) {
log.info { "[ScheduleValidator.validateCanDelete] 삭제 실패: id=${schedule.id} / status=${status}" } log.info { "[ScheduleValidator.validateCanDelete] 삭제 실패: id=${schedule.id} / status=${status}" }
throw ScheduleException(ScheduleErrorCode.SCHEDULE_IN_USE) throw ScheduleException(ScheduleErrorCode.SCHEDULE_IN_USE)
} }

View File

@ -1,10 +1,6 @@
package roomescape.schedule.infrastructure.persistence package roomescape.schedule.infrastructure.persistence
import jakarta.persistence.Entity import jakarta.persistence.*
import jakarta.persistence.EnumType
import jakarta.persistence.Enumerated
import jakarta.persistence.Table
import jakarta.persistence.UniqueConstraint
import roomescape.common.entity.AuditingBaseEntity import roomescape.common.entity.AuditingBaseEntity
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalTime import java.time.LocalTime

View File

@ -13,10 +13,12 @@ interface ScheduleRepository : JpaRepository<ScheduleEntity, Long> {
fun existsByDateAndThemeIdAndTime(date: LocalDate, themeId: Long, time: LocalTime): Boolean fun existsByDateAndThemeIdAndTime(date: LocalDate, themeId: Long, time: LocalTime): Boolean
@Query(""" @Query(
"""
SELECT DISTINCT s.themeId SELECT DISTINCT s.themeId
FROM ScheduleEntity s FROM ScheduleEntity s
WHERE s.date = :date WHERE s.date = :date
""") """
)
fun findAllUniqueThemeIdByDate(date: LocalDate): List<Long> fun findAllUniqueThemeIdByDate(date: LocalDate): List<Long>
} }

View File

@ -11,13 +11,7 @@ import org.springframework.web.bind.annotation.RequestBody
import roomescape.auth.web.support.Admin import roomescape.auth.web.support.Admin
import roomescape.auth.web.support.LoginRequired import roomescape.auth.web.support.LoginRequired
import roomescape.common.dto.response.CommonApiResponse import roomescape.common.dto.response.CommonApiResponse
import roomescape.theme.web.AdminThemeDetailRetrieveResponse import roomescape.theme.web.*
import roomescape.theme.web.AdminThemeSummaryRetrieveListResponse
import roomescape.theme.web.ThemeCreateRequest
import roomescape.theme.web.ThemeCreateResponseV2
import roomescape.theme.web.ThemeListRetrieveRequest
import roomescape.theme.web.ThemeUpdateRequest
import roomescape.theme.web.ThemeSummaryListResponse
@Tag(name = "5. 관리자 테마 API", description = "관리자 페이지에서 테마를 조회 / 추가 / 삭제할 때 사용합니다.") @Tag(name = "5. 관리자 테마 API", description = "관리자 페이지에서 테마를 조회 / 추가 / 삭제할 때 사용합니다.")
interface ThemeAPIV2 { interface ThemeAPIV2 {

View File

@ -1,10 +1,6 @@
package roomescape.theme.infrastructure.persistence package roomescape.theme.infrastructure.persistence
import jakarta.persistence.Column import jakarta.persistence.*
import jakarta.persistence.Entity
import jakarta.persistence.EnumType
import jakarta.persistence.Enumerated
import jakarta.persistence.Table
import roomescape.common.entity.AuditingBaseEntity import roomescape.common.entity.AuditingBaseEntity
@Entity @Entity

View File

@ -3,7 +3,7 @@ package roomescape.theme.infrastructure.persistence
import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query import org.springframework.data.jpa.repository.Query
interface ThemeRepository: JpaRepository<ThemeEntity, Long> { interface ThemeRepository : JpaRepository<ThemeEntity, Long> {
@Query("SELECT t FROM ThemeEntity t WHERE t.isOpen = true") @Query("SELECT t FROM ThemeEntity t WHERE t.isOpen = true")
fun findOpenedThemes(): List<ThemeEntity> fun findOpenedThemes(): List<ThemeEntity>

View File

@ -6,11 +6,7 @@ import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FunSpec import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import io.kotest.matchers.string.shouldContain import io.kotest.matchers.string.shouldContain
import java.time.LocalDate import java.time.*
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.OffsetDateTime
import java.time.ZoneOffset
class JacksonConfigTest( class JacksonConfigTest(
private val objectMapper: ObjectMapper = JacksonConfig().objectMapper() private val objectMapper: ObjectMapper = JacksonConfig().objectMapper()

View File

@ -1,7 +1,6 @@
package roomescape.common.log package roomescape.common.log
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import io.kotest.core.spec.style.FunSpec
import io.kotest.core.spec.style.StringSpec import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import io.mockk.every import io.mockk.every
@ -45,7 +44,11 @@ class ApiLogMessageConverterTest : StringSpec({
val requestURI = "/test/sangdol".also { every { request.requestURI } returns it } val requestURI = "/test/sangdol".also { every { request.requestURI } returns it }
converter.convertToControllerInvokedMessage(request, controllerPayload) shouldBe """ converter.convertToControllerInvokedMessage(request, controllerPayload) shouldBe """
{"type":"CONTROLLER_INVOKED","method":"$method","uri":"$requestURI","member_id":1,"controller_method":"${controllerPayload.get("controller_method")}","request_body":{"key1":"value1"}} {"type":"CONTROLLER_INVOKED","method":"$method","uri":"$requestURI","member_id":1,"controller_method":"${
controllerPayload.get(
"controller_method"
)
}","request_body":{"key1":"value1"}}
""".trimIndent() """.trimIndent()
} }

View File

@ -119,7 +119,7 @@ class TosspayClientTest(
assertSoftly(cancelResponse) { assertSoftly(cancelResponse) {
this.status shouldBe PaymentStatus.CANCELED this.status shouldBe PaymentStatus.CANCELED
with (this.cancels) { with(this.cancels) {
this.cancelAmount shouldBe SampleTosspayConstant.AMOUNT this.cancelAmount shouldBe SampleTosspayConstant.AMOUNT
this.cancelReason shouldBe SampleTosspayConstant.CANCEL_REASON this.cancelReason shouldBe SampleTosspayConstant.CANCEL_REASON
} }
@ -135,7 +135,6 @@ class TosspayClientTest(
.createResponse(it) .createResponse(it)
} }
val exception = shouldThrow<PaymentException> { val exception = shouldThrow<PaymentException> {
client.cancel( client.cancel(
SampleTosspayConstant.PAYMENT_KEY, SampleTosspayConstant.PAYMENT_KEY,

View File

@ -26,7 +26,7 @@ object KotestConfig : AbstractProjectConfig() {
@Import(TestConfig::class) @Import(TestConfig::class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
abstract class FunSpecSpringbootTest: FunSpec({ abstract class FunSpecSpringbootTest : FunSpec({
extension(DatabaseCleanerExtension(mode = AFTER_EACH_TEST)) extension(DatabaseCleanerExtension(mode = AFTER_EACH_TEST))
}) { }) {
@Autowired @Autowired