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

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

View File

@ -8,8 +8,12 @@ import jakarta.validation.Valid
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestParam
import roomescape.auth.web.support.Authenticated
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.common.dto.CurrentUserContext
import roomescape.common.dto.response.CommonApiResponse
import roomescape.payment.web.PaymentCancelRequest
import roomescape.payment.web.PaymentConfirmRequest
@ -17,7 +21,7 @@ import roomescape.payment.web.PaymentCreateResponse
interface PaymentAPI {
@LoginRequired
@UserOnly
@Operation(summary = "결제 승인", tags = ["로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
fun confirmPayment(
@ -25,11 +29,10 @@ interface PaymentAPI {
@Valid @RequestBody request: PaymentConfirmRequest
): ResponseEntity<CommonApiResponse<PaymentCreateResponse>>
@LoginRequired
@Operation(summary = "결제 취소", tags = ["로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
fun cancelPayment(
@MemberId @Parameter(hidden = true) memberId: Long,
@CurrentUser user: CurrentUserContext,
@Valid @RequestBody request: PaymentCancelRequest
): ResponseEntity<CommonApiResponse<Unit>>
}

View File

@ -7,7 +7,9 @@ import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import roomescape.auth.web.support.CurrentUser
import roomescape.auth.web.support.MemberId
import roomescape.common.dto.CurrentUserContext
import roomescape.common.dto.response.CommonApiResponse
import roomescape.payment.business.PaymentService
import roomescape.payment.docs.PaymentAPI
@ -29,10 +31,10 @@ class PaymentController(
@PostMapping("/payments/cancel")
override fun cancelPayment(
@MemberId @Parameter(hidden = true) memberId: Long,
@CurrentUser user: CurrentUserContext,
@Valid @RequestBody request: PaymentCancelRequest
): ResponseEntity<CommonApiResponse<Unit>> {
paymentService.cancel(memberId, request)
paymentService.cancel(user.id, request)
return ResponseEntity.ok(CommonApiResponse())
}

View File

@ -8,45 +8,46 @@ import jakarta.validation.Valid
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestBody
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.common.dto.CurrentUserContext
import roomescape.common.dto.response.CommonApiResponse
import roomescape.reservation.web.*
interface ReservationAPI {
@LoginRequired
@Operation(summary = "결제 대기 예약 저장", tags = ["로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
fun createPendingReservation(
@MemberId @Parameter(hidden = true) memberId: Long,
@CurrentUser user: CurrentUserContext,
@Valid @RequestBody request: PendingReservationCreateRequest
): ResponseEntity<CommonApiResponse<PendingReservationCreateResponse>>
@LoginRequired
@UserOnly
@Operation(summary = "예약 확정", tags = ["로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
fun confirmReservation(
@PathVariable("id") id: Long
): ResponseEntity<CommonApiResponse<Unit>>
@LoginRequired
@UserOnly
@Operation(summary = "예약 취소", tags = ["로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
fun cancelReservation(
@MemberId @Parameter(hidden = true) memberId: Long,
@CurrentUser user: CurrentUserContext,
@PathVariable reservationId: Long,
@Valid @RequestBody request: ReservationCancelRequest
): ResponseEntity<CommonApiResponse<Unit>>
@LoginRequired
@Operation(summary = "회원별 예약 요약 목록 조회", tags = ["로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
fun findSummaryByMemberId(
@MemberId @Parameter(hidden = true) memberId: Long
@CurrentUser user: CurrentUserContext,
): ResponseEntity<CommonApiResponse<ReservationSummaryRetrieveListResponse>>
@LoginRequired
@UserOnly
@Operation(summary = "특정 예약에 대한 상세 조회", tags = ["로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
fun findDetailById(

View File

@ -1,10 +1,10 @@
package roomescape.reservation.web
import io.swagger.v3.oas.annotations.Parameter
import jakarta.validation.Valid
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
import roomescape.auth.web.support.MemberId
import roomescape.auth.web.support.CurrentUser
import roomescape.common.dto.CurrentUserContext
import roomescape.common.dto.response.CommonApiResponse
import roomescape.reservation.business.ReservationService
import roomescape.reservation.docs.ReservationAPI
@ -16,10 +16,10 @@ class ReservationController(
@PostMapping("/reservations/pending")
override fun createPendingReservation(
@MemberId @Parameter(hidden = true) memberId: Long,
@CurrentUser user: CurrentUserContext,
@Valid @RequestBody request: PendingReservationCreateRequest
): ResponseEntity<CommonApiResponse<PendingReservationCreateResponse>> {
val response = reservationService.createPendingReservation(memberId, request)
val response = reservationService.createPendingReservation(user.id, request)
return ResponseEntity.ok(CommonApiResponse(response))
}
@ -35,20 +35,20 @@ class ReservationController(
@PostMapping("/reservations/{reservationId}/cancel")
override fun cancelReservation(
@MemberId @Parameter(hidden = true) memberId: Long,
@CurrentUser user: CurrentUserContext,
@PathVariable reservationId: Long,
@Valid @RequestBody request: ReservationCancelRequest
): ResponseEntity<CommonApiResponse<Unit>> {
reservationService.cancelReservation(memberId, reservationId, request)
reservationService.cancelReservation(user.id, reservationId, request)
return ResponseEntity.ok().body(CommonApiResponse())
}
@GetMapping("/reservations/summary")
override fun findSummaryByMemberId(
@MemberId @Parameter(hidden = true) memberId: Long
@CurrentUser user: CurrentUserContext,
): ResponseEntity<CommonApiResponse<ReservationSummaryRetrieveListResponse>> {
val response = reservationService.findSummaryByMemberId(memberId)
val response = reservationService.findSummaryByMemberId(user.id)
return ResponseEntity.ok(CommonApiResponse(response))
}

View File

@ -4,27 +4,31 @@ import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
import jakarta.validation.Valid
import org.aspectj.internal.lang.annotation.ajcPrivileged
import org.springframework.format.annotation.DateTimeFormat
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestParam
import roomescape.admin.infrastructure.persistence.Privilege
import roomescape.auth.web.support.Admin
import roomescape.auth.web.support.AdminOnly
import roomescape.auth.web.support.LoginRequired
import roomescape.auth.web.support.UserOnly
import roomescape.common.dto.response.CommonApiResponse
import roomescape.schedule.web.*
import java.time.LocalDate
interface ScheduleAPI {
@LoginRequired
@UserOnly
@Operation(summary = "입력된 날짜에 가능한 테마 목록 조회", tags = ["로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "입력된 날짜에 가능한 테마 목록 조회", useReturnTypeSchema = true))
fun findAvailableThemes(
@RequestParam("date") @DateTimeFormat(pattern = "yyyy-MM-dd") date: LocalDate
): ResponseEntity<CommonApiResponse<AvailableThemeIdListResponse>>
@LoginRequired
@UserOnly
@Operation(summary = "입력된 날짜, 테마에 대한 모든 시간 조회", tags = ["로그인이 필요한 API"])
@ApiResponses(
ApiResponse(
@ -38,7 +42,7 @@ interface ScheduleAPI {
@RequestParam("themeId") themeId: Long
): ResponseEntity<CommonApiResponse<ScheduleRetrieveListResponse>>
@LoginRequired
@UserOnly
@Operation(summary = "일정을 Hold 상태로 변경", tags = ["로그인이 필요한 API"])
@ApiResponses(
ApiResponse(
@ -51,21 +55,21 @@ interface ScheduleAPI {
@PathVariable("id") id: Long
): ResponseEntity<CommonApiResponse<Unit>>
@Admin
@AdminOnly(privilege = Privilege.READ_DETAIL)
@Operation(summary = "일정 상세 조회", tags = ["관리자 로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "감사 정보를 포함하여 일정 상세 조회", useReturnTypeSchema = true))
fun findScheduleDetail(
@PathVariable("id") id: Long
): ResponseEntity<CommonApiResponse<ScheduleDetailRetrieveResponse>>
@Admin
@AdminOnly(privilege = Privilege.CREATE)
@Operation(summary = "일정 생성", tags = ["관리자 로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
fun createSchedule(
@Valid @RequestBody request: ScheduleCreateRequest
): ResponseEntity<CommonApiResponse<ScheduleCreateResponse>>
@Admin
@AdminOnly(privilege = Privilege.UPDATE)
@Operation(summary = "일정 수정", tags = ["관리자 로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
fun updateSchedule(
@ -73,7 +77,7 @@ interface ScheduleAPI {
@Valid @RequestBody request: ScheduleUpdateRequest
): ResponseEntity<CommonApiResponse<Unit>>
@Admin
@AdminOnly(privilege = Privilege.DELETE)
@Operation(summary = "일정 삭제", tags = ["관리자 로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "204", description = "성공", useReturnTypeSchema = true))
fun deleteSchedule(

View File

@ -8,35 +8,38 @@ import jakarta.validation.Valid
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestBody
import roomescape.admin.infrastructure.persistence.Privilege
import roomescape.auth.web.support.Admin
import roomescape.auth.web.support.AdminOnly
import roomescape.auth.web.support.LoginRequired
import roomescape.auth.web.support.UserOnly
import roomescape.common.dto.response.CommonApiResponse
import roomescape.theme.web.*
@Tag(name = "5. 관리자 테마 API", description = "관리자 페이지에서 테마를 조회 / 추가 / 삭제할 때 사용합니다.")
interface ThemeAPIV2 {
@Admin
@AdminOnly(privilege = Privilege.READ_SUMMARY)
@Operation(summary = "모든 테마 조회", description = "관리자 페이지에서 요약된 테마 목록을 조회합니다.", tags = ["관리자 로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
fun findAdminThemes(): ResponseEntity<CommonApiResponse<AdminThemeSummaryRetrieveListResponse>>
@Admin
@AdminOnly(privilege = Privilege.READ_DETAIL)
@Operation(summary = "테마 상세 조회", description = "해당 테마의 상세 정보를 조회합니다.", tags = ["관리자 로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
fun findAdminThemeDetail(@PathVariable("id") id: Long): ResponseEntity<CommonApiResponse<AdminThemeDetailRetrieveResponse>>
@Admin
@AdminOnly(privilege = Privilege.CREATE)
@Operation(summary = "테마 추가", tags = ["관리자 로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
fun createTheme(@Valid @RequestBody themeCreateRequest: ThemeCreateRequest): ResponseEntity<CommonApiResponse<ThemeCreateResponseV2>>
@Admin
@AdminOnly(privilege = Privilege.DELETE)
@Operation(summary = "테마 삭제", tags = ["관리자 로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "204", description = "성공", useReturnTypeSchema = true))
fun deleteTheme(@PathVariable id: Long): ResponseEntity<CommonApiResponse<Unit>>
@Admin
@AdminOnly(privilege = Privilege.UPDATE)
@Operation(summary = "테마 수정", tags = ["관리자 로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
fun updateTheme(
@ -44,12 +47,12 @@ interface ThemeAPIV2 {
@Valid @RequestBody themeUpdateRequest: ThemeUpdateRequest
): ResponseEntity<CommonApiResponse<Unit>>
@LoginRequired
@UserOnly
@Operation(summary = "예약 페이지에서 모든 테마 조회", description = "모든 테마를 조회합니다.", tags = ["로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
fun findUserThemes(): ResponseEntity<CommonApiResponse<ThemeSummaryListResponse>>
@LoginRequired
@UserOnly
@Operation(summary = "예약 페이지에서 입력한 날짜에 가능한 테마 조회", description = "입력한 날짜에 가능한 테마를 조회합니다.", tags = ["로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
fun findThemesByIds(request: ThemeListRetrieveRequest): ResponseEntity<CommonApiResponse<ThemeSummaryListResponse>>

View File

@ -145,8 +145,8 @@ class PaymentAPITest(
PaymentMethod.entries.filter { it !in supportedMethod }.forEach {
test("결제 수단: ${it.koreanName}") {
val reservation = dummyInitializer.createConfirmReservation(
adminToken = authUtil.loginAsAdmin(),
reserverToken = authUtil.loginAsUser()
adminToken = authUtil.defaultAdminLogin(),
reserverToken = authUtil.defaultUserLogin()
)
val request = PaymentFixture.confirmRequest
@ -163,7 +163,7 @@ class PaymentAPITest(
)
runTest(
token = authUtil.loginAsUser(),
token = authUtil.defaultUserLogin(),
using = {
body(PaymentFixture.confirmRequest)
},
@ -182,7 +182,7 @@ class PaymentAPITest(
context("결제를 취소한다.") {
test("정상 취소") {
val token = authUtil.loginAsAdmin()
val token = authUtil.defaultAdminLogin()
val confirmRequest = PaymentFixture.confirmRequest
val reservation = dummyInitializer.createConfirmReservation(
adminToken = token,
@ -230,7 +230,7 @@ class PaymentAPITest(
}
test("예약에 대한 결제 정보가 없으면 실패한다.") {
val token = authUtil.loginAsAdmin()
val token = authUtil.defaultAdminLogin()
val reservation = dummyInitializer.createConfirmReservation(
adminToken = token,
reserverToken = token,
@ -282,8 +282,8 @@ class PaymentAPITest(
val request = PaymentFixture.confirmRequest.copy(paymentKey = paymentKey, amount = amount)
val reservation: ReservationEntity = dummyInitializer.createPendingReservation(
adminToken = authUtil.loginAsAdmin(),
reserverToken = authUtil.loginAsUser(),
adminToken = authUtil.defaultAdminLogin(),
reserverToken = authUtil.defaultUserLogin(),
)
val method = if (easyPayDetail != null) {
@ -305,7 +305,7 @@ class PaymentAPITest(
} returns clientResponse
runTest(
token = authUtil.loginAsUser(),
token = authUtil.defaultUserLogin(),
using = {
body(request)
},