[#44] 매장 기능 도입 #45

Merged
pricelees merged 116 commits from feat/#44 into main 2025-09-20 03:15:06 +00:00
9 changed files with 63 additions and 89 deletions
Showing only changes of commit 08af1c7084 - Show all commits

View File

@ -3,7 +3,6 @@ package roomescape.auth.docs
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
import io.swagger.v3.oas.annotations.tags.Tag
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import jakarta.validation.Valid
@ -11,28 +10,23 @@ import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.RequestBody
import roomescape.auth.web.LoginRequest
import roomescape.auth.web.LoginSuccessResponse
import roomescape.auth.web.support.User
import roomescape.auth.web.support.Public
import roomescape.auth.web.support.User
import roomescape.common.dto.CurrentUserContext
import roomescape.common.dto.response.CommonApiResponse
@Tag(name = "1. 인증 / 인가 API", description = "로그인, 로그아웃 및 로그인 상태를 확인합니다")
interface AuthAPI {
@Public
@Operation(summary = "로그인")
@ApiResponses(
ApiResponse(responseCode = "200", description = "로그인 성공시 토큰을 반환합니다."),
)
@ApiResponses(ApiResponse(responseCode = "200"))
fun login(
@Valid @RequestBody loginRequest: LoginRequest,
servletRequest: HttpServletRequest
): ResponseEntity<CommonApiResponse<LoginSuccessResponse>>
@Operation(summary = "로그아웃", tags = ["로그인이 필요한 API"])
@ApiResponses(
ApiResponse(responseCode = "200"),
)
@Operation(summary = "로그아웃")
@ApiResponses(ApiResponse(responseCode = "200"))
fun logout(
@User user: CurrentUserContext,
servletResponse: HttpServletResponse

View File

@ -18,16 +18,15 @@ import roomescape.payment.web.PaymentCreateResponse
interface PaymentAPI {
@UserOnly
@Operation(summary = "결제 승인", tags = ["로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
@Operation(summary = "결제 승인")
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
fun confirmPayment(
@RequestParam(required = true) reservationId: Long,
@Valid @RequestBody request: PaymentConfirmRequest
): ResponseEntity<CommonApiResponse<PaymentCreateResponse>>
@UserOnly
@Operation(summary = "결제 취소", tags = ["로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
@Operation(summary = "결제 취소")
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
fun cancelPayment(
@User user: CurrentUserContext,
@Valid @RequestBody request: PaymentCancelRequest

View File

@ -15,7 +15,7 @@ interface RegionAPI {
@Public
@Operation(summary = "지역 코드 조회")
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
fun findRegionCode(
@RequestParam(name = "sidoCode", required = true) sidoCode: String,
@RequestParam(name = "sigunguCode", required = true) sigunguCode: String,
@ -23,12 +23,12 @@ interface RegionAPI {
@Public
@Operation(summary = "모든 시 / 도 목록 조회")
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
fun readAllSido(): ResponseEntity<CommonApiResponse<SidoListResponse>>
@Public
@Operation(summary = "모든 시 / 군 / 구 목록 조회")
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
fun findAllSigunguBySido(
@RequestParam(required = true) sidoCode: String
): ResponseEntity<CommonApiResponse<SigunguListResponse>>

View File

@ -18,45 +18,43 @@ import roomescape.reservation.web.*
interface ReservationAPI {
@Public
@Operation(summary = "가장 많이 예약된 테마 조회")
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
@Operation(summary = "가장 많이 예약된 테마 ID 조회")
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
fun findMostReservedThemeIds(
@RequestParam count: Int
): ResponseEntity<CommonApiResponse<MostReservedThemeIdListResponse>>
@UserOnly
@Operation(summary = "결제 대기 예약 저장", tags = ["로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
@Operation(summary = "결제 전 임시 예약 저장")
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
fun createPendingReservation(
@User user: CurrentUserContext,
@Valid @RequestBody request: PendingReservationCreateRequest
): ResponseEntity<CommonApiResponse<PendingReservationCreateResponse>>
@UserOnly
@Operation(summary = "예약 확정", tags = ["로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
@Operation(summary = "결제 후 임시 예약 확정")
@ApiResponses(ApiResponse(responseCode = "200"))
fun confirmReservation(
@PathVariable("id") id: Long
): ResponseEntity<CommonApiResponse<Unit>>
@Operation(summary = "예약 취소", tags = ["로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
@Operation(summary = "예약 취소")
@ApiResponses(ApiResponse(responseCode = "200"))
fun cancelReservation(
@User user: CurrentUserContext,
@PathVariable id: Long,
@Valid @RequestBody request: ReservationCancelRequest
): ResponseEntity<CommonApiResponse<Unit>>
@UserOnly
@Operation(summary = "회원별 예약 요약 목록 조회", tags = ["로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
@Operation(summary = "회원별 예약 요약 목록 조회")
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
fun findSummaryByMemberId(
@User user: CurrentUserContext,
): ResponseEntity<CommonApiResponse<ReservationSummaryListResponse>>
@UserOnly
@Operation(summary = "특정 예약에 대한 상세 조회", tags = ["로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
@Operation(summary = "특정 예약에 대한 상세 조회")
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
fun findDetailById(
@PathVariable("id") id: Long
): ResponseEntity<CommonApiResponse<ReservationDetailResponse>>

View File

@ -22,7 +22,7 @@ import java.time.LocalDate
interface AdminScheduleAPI {
@AdminOnly(privilege = Privilege.READ_SUMMARY)
@Operation(summary = "일정 검색", tags = ["관리자 로그인이 필요한 API"])
@Operation(summary = "관리자 페이지에서 일정 요약 목록 조회")
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
fun searchSchedules(
@PathVariable("storeId") storeId: Long,
@ -31,14 +31,14 @@ interface AdminScheduleAPI {
): ResponseEntity<CommonApiResponse<AdminScheduleSummaryListResponse>>
@AdminOnly(privilege = Privilege.READ_DETAIL)
@Operation(summary = "일정 상세 조회", tags = ["관리자 로그인이 필요한 API"])
@Operation(summary = "관지라 페이지에서 특정 예약의 일정 상세 조회")
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
fun findScheduleAudit(
@PathVariable("id") id: Long
): ResponseEntity<CommonApiResponse<AuditInfo>>
@AdminOnly(type = AdminType.STORE, privilege = Privilege.CREATE)
@Operation(summary = "일정 생성", tags = ["관리자 로그인이 필요한 API"])
@Operation(summary = "일정 생성")
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
fun createSchedule(
@PathVariable("storeId") storeId: Long,
@ -46,16 +46,16 @@ interface AdminScheduleAPI {
): ResponseEntity<CommonApiResponse<ScheduleCreateResponse>>
@AdminOnly(type = AdminType.STORE, privilege = Privilege.UPDATE)
@Operation(summary = "일정 수정", tags = ["관리자 로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
@Operation(summary = "일정 수정")
@ApiResponses(ApiResponse(responseCode = "200"))
fun updateSchedule(
@PathVariable("id") id: Long,
@Valid @RequestBody request: ScheduleUpdateRequest
): ResponseEntity<CommonApiResponse<Unit>>
@AdminOnly(type = AdminType.STORE, privilege = Privilege.DELETE)
@Operation(summary = "일정 삭제", tags = ["관리자 로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "204", useReturnTypeSchema = true))
@Operation(summary = "일정 삭제")
@ApiResponses(ApiResponse(responseCode = "204"))
fun deleteSchedule(
@PathVariable("id") id: Long
): ResponseEntity<CommonApiResponse<Unit>>
@ -63,14 +63,8 @@ interface AdminScheduleAPI {
interface UserScheduleAPI {
@UserOnly
@Operation(summary = "일정을 Hold 상태로 변경", tags = ["로그인이 필요한 API"])
@ApiResponses(
ApiResponse(
responseCode = "200",
description = "일정을 Hold 상태로 변경하여 중복 예약 방지",
useReturnTypeSchema = true
)
)
@Operation(summary = "중복 방지를 위해 일정을 Hold 상태로 변경")
@ApiResponses(ApiResponse(responseCode = "200"))
fun holdSchedule(
@PathVariable("id") id: Long
): ResponseEntity<CommonApiResponse<Unit>>
@ -78,8 +72,8 @@ interface UserScheduleAPI {
interface PublicScheduleAPI {
@Public
@Operation(summary = "특정 날짜 + 매장의 일정 조회")
@ApiResponses(ApiResponse(useReturnTypeSchema = true))
@Operation(summary = "특정 날짜 + 매장의 모든 일정 조회")
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
fun getStoreSchedulesByDate(
@PathVariable("storeId") storeId: Long,
@RequestParam("date") @DateTimeFormat(pattern = "yyyy-MM-dd") date: LocalDate

View File

@ -32,7 +32,7 @@ interface AdminStoreAPI {
@AdminOnly(type = AdminType.STORE, privilege = Privilege.UPDATE)
@Operation(summary = "매장 정보 수정")
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
@ApiResponses(ApiResponse(responseCode = "200"))
fun updateStore(
@PathVariable id: Long,
@Valid @RequestBody request: StoreUpdateRequest
@ -40,7 +40,7 @@ interface AdminStoreAPI {
@AdminOnly(type = AdminType.HQ, privilege = Privilege.DELETE)
@Operation(summary = "매장 비활성화")
@ApiResponses(ApiResponse(responseCode = "204", useReturnTypeSchema = true))
@ApiResponses(ApiResponse(responseCode = "204"))
fun disableStore(
@PathVariable id: Long
): ResponseEntity<CommonApiResponse<Unit>>

View File

@ -1,5 +1,6 @@
package roomescape.store.infrastructure.persistence
import com.fasterxml.jackson.databind.util.Named
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
@ -26,8 +27,13 @@ interface StoreRepository : JpaRepository<StoreEntity, Long> {
StoreEntity s
WHERE
s.status = roomescape.store.infrastructure.persistence.StoreStatus.ACTIVE
AND (:regionCode IS NULL OR s.regionCode LIKE ':regionCode%')
AND (:regionCode IS NULL OR s.regionCode LIKE :regionCode%)
"""
)
fun findAllActiveStoresByRegion(regionCode: String?): List<StoreEntity>
fun existsByName(name: String): Boolean
fun existsByContact(contact: String): Boolean
fun existsByAddress(address: String): Boolean
fun existsByBusinessRegNum(businessRegNum: String): Boolean
}

View File

@ -15,49 +15,49 @@ import roomescape.auth.web.support.Public
import roomescape.common.dto.response.CommonApiResponse
import roomescape.theme.web.*
@Tag(name = "5. 관리자 테마 API", description = "관리자 페이지에서 테마를 조회 / 추가 / 삭제할 때 사용합니다.")
interface AdminThemeAPI {
@AdminOnly(type = AdminType.HQ, privilege = Privilege.READ_SUMMARY)
@Operation(summary = "모든 테마 조회", description = "관리자 페이지에서 요약된 테마 목록을 조회합니다.", tags = ["관리자 로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
@Operation(summary = "모든 테마 조회")
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
fun getAdminThemeSummaries(): ResponseEntity<CommonApiResponse<AdminThemeSummaryListResponse>>
@AdminOnly(type = AdminType.HQ, privilege = Privilege.READ_DETAIL)
@Operation(summary = "테마 상세 조회", description = "해당 테마의 상세 정보를 조회합니다.", tags = ["관리자 로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
@Operation(summary = "테마 상세 조회")
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
fun findAdminThemeDetail(@PathVariable("id") id: Long): ResponseEntity<CommonApiResponse<AdminThemeDetailResponse>>
@AdminOnly(type = AdminType.HQ, privilege = Privilege.CREATE)
@Operation(summary = "테마 추가", tags = ["관리자 로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
@Operation(summary = "테마 추가")
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
fun createTheme(@Valid @RequestBody themeCreateRequest: ThemeCreateRequest): ResponseEntity<CommonApiResponse<ThemeCreateResponse>>
@AdminOnly(type = AdminType.HQ, privilege = Privilege.DELETE)
@Operation(summary = "테마 삭제", tags = ["관리자 로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "204", description = "성공", useReturnTypeSchema = true))
@Operation(summary = "테마 삭제")
@ApiResponses(ApiResponse(responseCode = "204"))
fun deleteTheme(@PathVariable id: Long): ResponseEntity<CommonApiResponse<Unit>>
@AdminOnly(type = AdminType.HQ, privilege = Privilege.UPDATE)
@Operation(summary = "테마 수정", tags = ["관리자 로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
@Operation(summary = "테마 수정")
@ApiResponses(ApiResponse(responseCode = "200"))
fun updateTheme(
@PathVariable id: Long,
@Valid @RequestBody request: ThemeUpdateRequest
): ResponseEntity<CommonApiResponse<Unit>>
@AdminOnly(privilege = Privilege.READ_SUMMARY)
@Operation(summary = "테마 조회", description = "현재 open 상태인 모든 테마 ID + 이름 조회", tags = ["관리자 로그인이 필요한 API"])
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
@Operation(summary = "현재 활성화 상태인 테마 ID + 이름 목록 조회")
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
fun getActiveThemes(): ResponseEntity<CommonApiResponse<SimpleActiveThemeListResponse>>
}
interface PublicThemeAPI {
@Public
@Operation(summary = "입력된 모든 ID에 대한 테마 조회")
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
@Operation(summary = "입력된 모든 ID에 대한 테마 정보 조회")
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
fun findThemeInfosByIds(request: ThemeIdListRequest): ResponseEntity<CommonApiResponse<ThemeInfoListResponse>>
@Public
@Operation(summary = "입력된 테마 ID에 대한 정보 조회")
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
fun findThemeInfoById(@PathVariable id: Long): ResponseEntity<CommonApiResponse<ThemeInfoResponse>>
}

View File

@ -3,46 +3,29 @@ package roomescape.user.docs
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
import io.swagger.v3.oas.annotations.tags.Tag
import jakarta.validation.Valid
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.RequestBody
import roomescape.auth.web.support.User
import roomescape.auth.web.support.Public
import roomescape.auth.web.support.UserOnly
import roomescape.auth.web.support.User
import roomescape.common.dto.CurrentUserContext
import roomescape.common.dto.response.CommonApiResponse
import roomescape.user.web.UserContactResponse
import roomescape.user.web.UserCreateRequest
import roomescape.user.web.UserCreateResponse
@Tag(name = "2. 회원 API", description = "회원 정보를 관리할 때 사용합니다.")
interface UserAPI {
@Public
@Operation(summary = "회원 가입")
@ApiResponses(
ApiResponse(
responseCode = "200",
description = "성공",
useReturnTypeSchema = true
)
)
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
fun signup(
@Valid @RequestBody request: UserCreateRequest
): ResponseEntity<CommonApiResponse<UserCreateResponse>>
@UserOnly
@Operation(summary = "회원 가입")
@ApiResponses(
ApiResponse(
responseCode = "200",
description = "성공",
useReturnTypeSchema = true
)
)
@Operation(summary = "회원 연락처 조회")
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
fun findContact(
@User user: CurrentUserContext
): ResponseEntity<CommonApiResponse<UserContactResponse>>
}