[#34] 회원 / 인증 도메인 재정의 #43

Merged
pricelees merged 73 commits from refactor/#34 into main 2025-09-13 10:13:45 +00:00
4 changed files with 31 additions and 31 deletions
Showing only changes of commit 53d82902ca - Show all commits

View File

@ -7,9 +7,10 @@ import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional import org.springframework.transaction.annotation.Transactional
import roomescape.common.config.next import roomescape.common.config.next
import roomescape.member.business.MemberService import roomescape.common.dto.CurrentUserContext
import roomescape.member.infrastructure.persistence.Role import roomescape.common.dto.PrincipalType
import roomescape.member.web.MemberSummaryRetrieveResponse import roomescape.member.business.UserService
import roomescape.member.web.UserContactRetrieveResponse
import roomescape.payment.business.PaymentService import roomescape.payment.business.PaymentService
import roomescape.payment.web.PaymentRetrieveResponse import roomescape.payment.web.PaymentRetrieveResponse
import roomescape.reservation.exception.ReservationErrorCode import roomescape.reservation.exception.ReservationErrorCode
@ -31,7 +32,7 @@ class ReservationService(
private val reservationRepository: ReservationRepository, private val reservationRepository: ReservationRepository,
private val reservationValidator: ReservationValidator, private val reservationValidator: ReservationValidator,
private val scheduleService: ScheduleService, private val scheduleService: ScheduleService,
private val memberService: MemberService, private val userService: UserService,
private val themeService: ThemeService, private val themeService: ThemeService,
private val canceledReservationRepository: CanceledReservationRepository, private val canceledReservationRepository: CanceledReservationRepository,
private val tsidFactory: TsidFactory, private val tsidFactory: TsidFactory,
@ -40,14 +41,14 @@ class ReservationService(
@Transactional @Transactional
fun createPendingReservation( fun createPendingReservation(
memberId: Long, user: CurrentUserContext,
request: PendingReservationCreateRequest request: PendingReservationCreateRequest
): PendingReservationCreateResponse { ): PendingReservationCreateResponse {
log.info { "[ReservationService.createPendingReservation] Pending 예약 생성 시작: schedule=${request.scheduleId}" } log.info { "[ReservationService.createPendingReservation] Pending 예약 생성 시작: schedule=${request.scheduleId}" }
validateCanCreate(request) validateCanCreate(request)
val reservation: ReservationEntity = request.toEntity(id = tsidFactory.next(), memberId = memberId) val reservation: ReservationEntity = request.toEntity(id = tsidFactory.next(), userId = user.id)
return PendingReservationCreateResponse(reservationRepository.save(reservation).id) return PendingReservationCreateResponse(reservationRepository.save(reservation).id)
.also { "[ReservationService.createPendingReservation] Pending 예약 생성 완료: reservationId=${it}, schedule=${request.scheduleId}" } .also { "[ReservationService.createPendingReservation] Pending 예약 생성 완료: reservationId=${it}, schedule=${request.scheduleId}" }
@ -70,18 +71,17 @@ class ReservationService(
} }
@Transactional @Transactional
fun cancelReservation(memberId: Long, reservationId: Long, request: ReservationCancelRequest) { fun cancelReservation(user: CurrentUserContext, reservationId: Long, request: ReservationCancelRequest) {
log.info { "[ReservationService.cancelReservation] 예약 취소 시작: memberId=${memberId}, reservationId=${reservationId}" } log.info { "[ReservationService.cancelReservation] 예약 취소 시작: userId=${user.id}, reservationId=${reservationId}" }
val reservation: ReservationEntity = findOrThrow(reservationId) val reservation: ReservationEntity = findOrThrow(reservationId)
val member: MemberSummaryRetrieveResponse = memberService.findSummaryById(memberId)
run { run {
scheduleService.updateSchedule( scheduleService.updateSchedule(
reservation.scheduleId, reservation.scheduleId,
ScheduleUpdateRequest(status = ScheduleStatus.AVAILABLE) ScheduleUpdateRequest(status = ScheduleStatus.AVAILABLE)
) )
saveCanceledReservation(member, reservation, request.cancelReason) saveCanceledReservation(user, reservation, request.cancelReason)
reservation.cancel() reservation.cancel()
}.also { }.also {
log.info { "[ReservationService.cancelReservation] 예약 취소 완료: reservationId=${reservationId}" } log.info { "[ReservationService.cancelReservation] 예약 취소 완료: reservationId=${reservationId}" }
@ -89,10 +89,10 @@ class ReservationService(
} }
@Transactional(readOnly = true) @Transactional(readOnly = true)
fun findSummaryByMemberId(memberId: Long): ReservationSummaryRetrieveListResponse { fun findUserSummaryReservation(user: CurrentUserContext): ReservationSummaryRetrieveListResponse {
log.info { "[ReservationService.findSummaryByMemberId] 예약 조회 시작: memberId=${memberId}" } log.info { "[ReservationService.findSummaryByMemberId] 예약 조회 시작: userId=${user.id}" }
val reservations: List<ReservationEntity> = reservationRepository.findAllByMemberId(memberId) val reservations: List<ReservationEntity> = reservationRepository.findAllByUserId(user.id)
return ReservationSummaryRetrieveListResponse(reservations.map { return ReservationSummaryRetrieveListResponse(reservations.map {
val schedule: ScheduleSummaryResponse = scheduleService.findSummaryById(it.scheduleId) val schedule: ScheduleSummaryResponse = scheduleService.findSummaryById(it.scheduleId)
@ -106,7 +106,7 @@ class ReservationService(
status = it.status status = it.status
) )
}).also { }).also {
log.info { "[ReservationService.findSummaryByMemberId] ${it.reservations.size}개의 예약 조회 완료: memberId=${memberId}" } log.info { "[ReservationService.findSummaryByMemberId] ${it.reservations.size}개의 예약 조회 완료: userId=${user.id}" }
} }
} }
@ -115,11 +115,11 @@ class ReservationService(
log.info { "[ReservationService.findDetailById] 예약 상세 조회 시작: reservationId=${id}" } log.info { "[ReservationService.findDetailById] 예약 상세 조회 시작: reservationId=${id}" }
val reservation: ReservationEntity = findOrThrow(id) val reservation: ReservationEntity = findOrThrow(id)
val member: MemberSummaryRetrieveResponse = memberService.findSummaryById(reservation.memberId) val user: UserContactRetrieveResponse = userService.findContactById(reservation.userId)
val paymentDetail: PaymentRetrieveResponse = paymentService.findDetailByReservationId(id) val paymentDetail: PaymentRetrieveResponse = paymentService.findDetailByReservationId(id)
return reservation.toReservationDetailRetrieveResponse( return reservation.toReservationDetailRetrieveResponse(
member = member, user = user,
payment = paymentDetail payment = paymentDetail
).also { ).also {
log.info { "[ReservationService.findDetailById] 예약 상세 조회 완료: reservationId=${id}" } log.info { "[ReservationService.findDetailById] 예약 상세 조회 완료: reservationId=${id}" }
@ -138,19 +138,19 @@ class ReservationService(
} }
private fun saveCanceledReservation( private fun saveCanceledReservation(
member: MemberSummaryRetrieveResponse, user: CurrentUserContext,
reservation: ReservationEntity, reservation: ReservationEntity,
cancelReason: String cancelReason: String
) { ) {
if (member.role != Role.ADMIN && reservation.memberId != member.id) { if (user.type != PrincipalType.ADMIN && reservation.userId != user.id) {
log.warn { "[ReservationService.createCanceledPayment] 예약자 본인 또는 관리자가 아닌 회원의 취소 요청: reservationId=${reservation.id}, memberId=${member.id}" } log.warn { "[ReservationService.createCanceledPayment] 예약자 본인 또는 관리자가 아닌 회원의 취소 요청: reservationId=${reservation.id}, userId=${user.id}" }
throw ReservationException(ReservationErrorCode.NO_PERMISSION_TO_CANCEL_RESERVATION) throw ReservationException(ReservationErrorCode.NO_PERMISSION_TO_CANCEL_RESERVATION)
} }
CanceledReservationEntity( CanceledReservationEntity(
id = tsidFactory.next(), id = tsidFactory.next(),
reservationId = reservation.id, reservationId = reservation.id,
canceledBy = member.id, canceledBy = user.id,
cancelReason = cancelReason, cancelReason = cancelReason,
canceledAt = LocalDateTime.now(), canceledAt = LocalDateTime.now(),
status = CanceledReservationStatus.PROCESSING status = CanceledReservationStatus.PROCESSING

View File

@ -1,16 +1,14 @@
package roomescape.reservation.docs package roomescape.reservation.docs
import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.responses.ApiResponse import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses import io.swagger.v3.oas.annotations.responses.ApiResponses
import jakarta.validation.Valid import jakarta.validation.Valid
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestBody
import roomescape.auth.web.support.Authenticated
import roomescape.auth.web.support.CurrentUser import roomescape.auth.web.support.CurrentUser
import roomescape.auth.web.support.LoginRequired
import roomescape.auth.web.support.MemberId
import roomescape.auth.web.support.UserOnly import roomescape.auth.web.support.UserOnly
import roomescape.common.dto.CurrentUserContext import roomescape.common.dto.CurrentUserContext
import roomescape.common.dto.response.CommonApiResponse import roomescape.common.dto.response.CommonApiResponse
@ -18,6 +16,7 @@ import roomescape.reservation.web.*
interface ReservationAPI { interface ReservationAPI {
@UserOnly
@Operation(summary = "결제 대기 예약 저장", tags = ["로그인이 필요한 API"]) @Operation(summary = "결제 대기 예약 저장", tags = ["로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true)) @ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
fun createPendingReservation( fun createPendingReservation(
@ -32,7 +31,7 @@ interface ReservationAPI {
@PathVariable("id") id: Long @PathVariable("id") id: Long
): ResponseEntity<CommonApiResponse<Unit>> ): ResponseEntity<CommonApiResponse<Unit>>
@UserOnly @Authenticated
@Operation(summary = "예약 취소", tags = ["로그인이 필요한 API"]) @Operation(summary = "예약 취소", tags = ["로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true)) @ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
fun cancelReservation( fun cancelReservation(
@ -41,6 +40,7 @@ interface ReservationAPI {
@Valid @RequestBody request: ReservationCancelRequest @Valid @RequestBody request: ReservationCancelRequest
): ResponseEntity<CommonApiResponse<Unit>> ): ResponseEntity<CommonApiResponse<Unit>>
@UserOnly
@Operation(summary = "회원별 예약 요약 목록 조회", tags = ["로그인이 필요한 API"]) @Operation(summary = "회원별 예약 요약 목록 조회", tags = ["로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true)) @ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
fun findSummaryByMemberId( fun findSummaryByMemberId(

View File

@ -19,7 +19,7 @@ class ReservationController(
@CurrentUser user: CurrentUserContext, @CurrentUser user: CurrentUserContext,
@Valid @RequestBody request: PendingReservationCreateRequest @Valid @RequestBody request: PendingReservationCreateRequest
): ResponseEntity<CommonApiResponse<PendingReservationCreateResponse>> { ): ResponseEntity<CommonApiResponse<PendingReservationCreateResponse>> {
val response = reservationService.createPendingReservation(user.id, request) val response = reservationService.createPendingReservation(user, request)
return ResponseEntity.ok(CommonApiResponse(response)) return ResponseEntity.ok(CommonApiResponse(response))
} }
@ -39,7 +39,7 @@ class ReservationController(
@PathVariable reservationId: Long, @PathVariable reservationId: Long,
@Valid @RequestBody request: ReservationCancelRequest @Valid @RequestBody request: ReservationCancelRequest
): ResponseEntity<CommonApiResponse<Unit>> { ): ResponseEntity<CommonApiResponse<Unit>> {
reservationService.cancelReservation(user.id, reservationId, request) reservationService.cancelReservation(user, reservationId, request)
return ResponseEntity.ok().body(CommonApiResponse()) return ResponseEntity.ok().body(CommonApiResponse())
} }
@ -48,7 +48,7 @@ class ReservationController(
override fun findSummaryByMemberId( override fun findSummaryByMemberId(
@CurrentUser user: CurrentUserContext, @CurrentUser user: CurrentUserContext,
): ResponseEntity<CommonApiResponse<ReservationSummaryRetrieveListResponse>> { ): ResponseEntity<CommonApiResponse<ReservationSummaryRetrieveListResponse>> {
val response = reservationService.findSummaryByMemberId(user.id) val response = reservationService.findUserSummaryReservation(user)
return ResponseEntity.ok(CommonApiResponse(response)) return ResponseEntity.ok(CommonApiResponse(response))
} }

View File

@ -1,7 +1,7 @@
package roomescape.reservation.web package roomescape.reservation.web
import jakarta.validation.constraints.NotEmpty import jakarta.validation.constraints.NotEmpty
import roomescape.member.web.MemberSummaryRetrieveResponse import roomescape.member.web.UserContactRetrieveResponse
import roomescape.payment.web.PaymentRetrieveResponse import roomescape.payment.web.PaymentRetrieveResponse
import roomescape.reservation.infrastructure.persistence.ReservationEntity import roomescape.reservation.infrastructure.persistence.ReservationEntity
import roomescape.reservation.infrastructure.persistence.ReservationStatus import roomescape.reservation.infrastructure.persistence.ReservationStatus
@ -48,18 +48,18 @@ data class ReservationSummaryRetrieveListResponse(
data class ReservationDetailRetrieveResponse( data class ReservationDetailRetrieveResponse(
val id: Long, val id: Long,
val member: MemberSummaryRetrieveResponse, val user: UserContactRetrieveResponse,
val applicationDateTime: LocalDateTime, val applicationDateTime: LocalDateTime,
val payment: PaymentRetrieveResponse, val payment: PaymentRetrieveResponse,
) )
fun ReservationEntity.toReservationDetailRetrieveResponse( fun ReservationEntity.toReservationDetailRetrieveResponse(
member: MemberSummaryRetrieveResponse, user: UserContactRetrieveResponse,
payment: PaymentRetrieveResponse, payment: PaymentRetrieveResponse,
): ReservationDetailRetrieveResponse { ): ReservationDetailRetrieveResponse {
return ReservationDetailRetrieveResponse( return ReservationDetailRetrieveResponse(
id = this.id, id = this.id,
member = member, user = user,
applicationDateTime = this.createdAt, applicationDateTime = this.createdAt,
payment = payment, payment = payment,
) )