generated from pricelees/issue-pr-template
[#44] 매장 기능 도입 #45
@ -34,5 +34,5 @@ export const fetchUserThemes = async (): Promise<ThemeInfoListResponse> => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const findThemesByIds = async (request: ThemeIdListResponse): Promise<ThemeInfoListResponse> => {
|
export const findThemesByIds = async (request: ThemeIdListResponse): Promise<ThemeInfoListResponse> => {
|
||||||
return await apiClient.post<ThemeInfoListResponse>('/themes/retrieve', request);
|
return await apiClient.post<ThemeInfoListResponse>('/themes/batch', request);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -42,7 +42,7 @@ class ThemeService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
fun findThemesByIds(request: ThemeIdListResponse): ThemeInfoListResponse {
|
fun findThemesByIds(request: ThemeIdListRequest): ThemeInfoListResponse {
|
||||||
log.info { "[ThemeService.findThemesByIds] 예약 페이지에서의 테마 목록 조회 시작: themeIds=${request.themeIds}" }
|
log.info { "[ThemeService.findThemesByIds] 예약 페이지에서의 테마 목록 조회 시작: themeIds=${request.themeIds}" }
|
||||||
val result: MutableList<ThemeEntity> = mutableListOf()
|
val result: MutableList<ThemeEntity> = mutableListOf()
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ class ThemeService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
fun createTheme(request: ThemeCreateRequest): ThemeCreateResponseV2 {
|
fun createTheme(request: ThemeCreateRequest): ThemeCreateResponse {
|
||||||
log.info { "[ThemeService.createTheme] 테마 생성 시작: name=${request.name}" }
|
log.info { "[ThemeService.createTheme] 테마 생성 시작: name=${request.name}" }
|
||||||
|
|
||||||
themeValidator.validateCanCreate(request)
|
themeValidator.validateCanCreate(request)
|
||||||
@ -95,7 +95,7 @@ class ThemeService(
|
|||||||
request.toEntity(tsidFactory.next())
|
request.toEntity(tsidFactory.next())
|
||||||
)
|
)
|
||||||
|
|
||||||
return ThemeCreateResponseV2(theme.id).also {
|
return ThemeCreateResponse(theme.id).also {
|
||||||
log.info { "[ThemeService.createTheme] 테마 생성 완료: id=${theme.id}, name=${theme.name}" }
|
log.info { "[ThemeService.createTheme] 테마 생성 완료: id=${theme.id}, name=${theme.name}" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,7 +30,7 @@ interface HQAdminThemeAPI {
|
|||||||
@AdminOnly(type = AdminType.HQ, privilege = Privilege.CREATE)
|
@AdminOnly(type = AdminType.HQ, privilege = Privilege.CREATE)
|
||||||
@Operation(summary = "테마 추가", tags = ["관리자 로그인이 필요한 API"])
|
@Operation(summary = "테마 추가", tags = ["관리자 로그인이 필요한 API"])
|
||||||
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
|
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
|
||||||
fun createTheme(@Valid @RequestBody themeCreateRequest: ThemeCreateRequest): ResponseEntity<CommonApiResponse<ThemeCreateResponseV2>>
|
fun createTheme(@Valid @RequestBody themeCreateRequest: ThemeCreateRequest): ResponseEntity<CommonApiResponse<ThemeCreateResponse>>
|
||||||
|
|
||||||
@AdminOnly(type = AdminType.HQ, privilege = Privilege.DELETE)
|
@AdminOnly(type = AdminType.HQ, privilege = Privilege.DELETE)
|
||||||
@Operation(summary = "테마 삭제", tags = ["관리자 로그인이 필요한 API"])
|
@Operation(summary = "테마 삭제", tags = ["관리자 로그인이 필요한 API"])
|
||||||
@ -57,7 +57,7 @@ interface PublicThemeAPI {
|
|||||||
@Public
|
@Public
|
||||||
@Operation(summary = "입력된 모든 ID에 대한 테마 조회")
|
@Operation(summary = "입력된 모든 ID에 대한 테마 조회")
|
||||||
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
|
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
|
||||||
fun findThemesByIds(request: ThemeIdListResponse): ResponseEntity<CommonApiResponse<ThemeInfoListResponse>>
|
fun findThemesByIds(request: ThemeIdListRequest): ResponseEntity<CommonApiResponse<ThemeInfoListResponse>>
|
||||||
|
|
||||||
@Public
|
@Public
|
||||||
@Operation(summary = "입력된 테마 ID에 대한 정보 조회")
|
@Operation(summary = "입력된 테마 ID에 대한 정보 조회")
|
||||||
|
|||||||
@ -27,7 +27,7 @@ class HQAdminThemeController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/admin/themes")
|
@PostMapping("/admin/themes")
|
||||||
override fun createTheme(themeCreateRequest: ThemeCreateRequest): ResponseEntity<CommonApiResponse<ThemeCreateResponseV2>> {
|
override fun createTheme(themeCreateRequest: ThemeCreateRequest): ResponseEntity<CommonApiResponse<ThemeCreateResponse>> {
|
||||||
val response = themeService.createTheme(themeCreateRequest)
|
val response = themeService.createTheme(themeCreateRequest)
|
||||||
|
|
||||||
return ResponseEntity.created(URI.create("/admin/themes/${response.id}"))
|
return ResponseEntity.created(URI.create("/admin/themes/${response.id}"))
|
||||||
|
|||||||
@ -31,7 +31,7 @@ data class ThemeCreateRequest(
|
|||||||
val isActive: Boolean
|
val isActive: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
data class ThemeCreateResponseV2(
|
data class ThemeCreateResponse(
|
||||||
val id: Long
|
val id: Long
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -145,38 +145,20 @@ fun ThemeEntity.toAdminThemeDetailResponse(createdBy: OperatorInfo, updatedBy: O
|
|||||||
// Store Admin DTO
|
// Store Admin DTO
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
||||||
data class ThemeInfoResponse(
|
data class SimpleActiveThemeResponse(
|
||||||
val id: Long,
|
val id: Long,
|
||||||
val name: String,
|
val name: String
|
||||||
val thumbnailUrl: String,
|
|
||||||
val description: String,
|
|
||||||
val difficulty: Difficulty,
|
|
||||||
val price: Int,
|
|
||||||
val minParticipants: Short,
|
|
||||||
val maxParticipants: Short,
|
|
||||||
val availableMinutes: Short,
|
|
||||||
val expectedMinutesFrom: Short,
|
|
||||||
val expectedMinutesTo: Short
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun ThemeEntity.toSummaryResponse() = ThemeInfoResponse(
|
fun ThemeEntity.toSimpleActiveThemeResponse() = SimpleActiveThemeResponse(
|
||||||
id = this.id,
|
id = this.id,
|
||||||
name = this.name,
|
name = this.name
|
||||||
thumbnailUrl = this.thumbnailUrl,
|
|
||||||
description = this.description,
|
|
||||||
difficulty = this.difficulty,
|
|
||||||
price = this.price,
|
|
||||||
minParticipants = this.minParticipants,
|
|
||||||
maxParticipants = this.maxParticipants,
|
|
||||||
availableMinutes = this.availableMinutes,
|
|
||||||
expectedMinutesFrom = this.expectedMinutesFrom,
|
|
||||||
expectedMinutesTo = this.expectedMinutesTo
|
|
||||||
)
|
)
|
||||||
|
|
||||||
data class ThemeInfoListResponse(
|
data class SimpleActiveThemeListResponse(
|
||||||
val themes: List<ThemeInfoResponse>
|
val themes: List<SimpleActiveThemeResponse>
|
||||||
)
|
)
|
||||||
|
|
||||||
fun List<ThemeEntity>.toListResponse() = ThemeInfoListResponse(
|
fun List<ThemeEntity>.toSimpleActiveThemeResponse() = SimpleActiveThemeListResponse(
|
||||||
themes = this.map { it.toSummaryResponse() }
|
themes = this.map { it.toSimpleActiveThemeResponse() }
|
||||||
)
|
)
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import roomescape.theme.business.MIN_PRICE
|
|||||||
import roomescape.theme.exception.ThemeErrorCode
|
import roomescape.theme.exception.ThemeErrorCode
|
||||||
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
||||||
import roomescape.theme.infrastructure.persistence.ThemeRepository
|
import roomescape.theme.infrastructure.persistence.ThemeRepository
|
||||||
import roomescape.theme.web.ThemeIdListResponse
|
import roomescape.theme.web.ThemeIdListRequest
|
||||||
import roomescape.theme.web.ThemeUpdateRequest
|
import roomescape.theme.web.ThemeUpdateRequest
|
||||||
import roomescape.supports.*
|
import roomescape.supports.*
|
||||||
import roomescape.supports.ThemeFixture.createRequest
|
import roomescape.supports.ThemeFixture.createRequest
|
||||||
@ -311,10 +311,10 @@ class ThemeApiTest(
|
|||||||
runTest(
|
runTest(
|
||||||
token = authUtil.defaultUserLogin(),
|
token = authUtil.defaultUserLogin(),
|
||||||
using = {
|
using = {
|
||||||
body(ThemeIdListResponse(themeIds))
|
body(ThemeIdListRequest(themeIds))
|
||||||
},
|
},
|
||||||
on = {
|
on = {
|
||||||
post("/themes/retrieve")
|
post("/themes/batch")
|
||||||
},
|
},
|
||||||
expect = {
|
expect = {
|
||||||
statusCode(HttpStatus.OK.value())
|
statusCode(HttpStatus.OK.value())
|
||||||
@ -337,10 +337,10 @@ class ThemeApiTest(
|
|||||||
runTest(
|
runTest(
|
||||||
token = authUtil.defaultUserLogin(),
|
token = authUtil.defaultUserLogin(),
|
||||||
using = {
|
using = {
|
||||||
body(ThemeIdListResponse(themeIds))
|
body(ThemeIdListRequest(themeIds))
|
||||||
},
|
},
|
||||||
on = {
|
on = {
|
||||||
post("/themes/retrieve")
|
post("/themes/batch")
|
||||||
},
|
},
|
||||||
expect = {
|
expect = {
|
||||||
statusCode(HttpStatus.OK.value())
|
statusCode(HttpStatus.OK.value())
|
||||||
@ -411,35 +411,38 @@ class ThemeApiTest(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context("예약 페이지에서 테마를 조회한다.") {
|
context("ID로 테마 정보를 조회한다.") {
|
||||||
test("공개된 테마의 전체 정보가 조회된다.") {
|
test("성공 응답") {
|
||||||
val token = authUtil.defaultHqAdminLogin()
|
val createdTheme: ThemeEntity = dummyInitializer.createTheme(
|
||||||
listOf(
|
adminToken = authUtil.defaultHqAdminLogin(),
|
||||||
createRequest.copy(name = "open", isOpen = true),
|
request = createRequest
|
||||||
createRequest.copy(name = "close", isOpen = false)
|
)
|
||||||
).forEach {
|
|
||||||
dummyInitializer.createTheme(token, it)
|
|
||||||
}
|
|
||||||
|
|
||||||
runTest(
|
runTest(
|
||||||
token = authUtil.defaultUserLogin(),
|
|
||||||
on = {
|
on = {
|
||||||
get("/themes")
|
get("/themes/${createdTheme.id}")
|
||||||
},
|
},
|
||||||
expect = {
|
expect = {
|
||||||
body("data.themes.size()", equalTo(1))
|
body("data.id", equalTo(createdTheme.id))
|
||||||
body("data.themes[0].name", equalTo("open"))
|
body("data.name", equalTo(createdTheme.name))
|
||||||
assertProperties(
|
assertProperties(
|
||||||
props = setOf(
|
props = setOf(
|
||||||
"id", "name", "thumbnailUrl", "description", "difficulty", "price",
|
"id", "name", "thumbnailUrl", "description", "difficulty", "price",
|
||||||
"minParticipants", "maxParticipants",
|
"minParticipants", "maxParticipants",
|
||||||
"availableMinutes", "expectedMinutesFrom", "expectedMinutesTo"
|
"availableMinutes", "expectedMinutesFrom", "expectedMinutesTo"
|
||||||
),
|
),
|
||||||
propsNameIfList = "themes",
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("테마가 없으면 실패한다.") {
|
||||||
|
runExceptionTest(
|
||||||
|
method = HttpMethod.GET,
|
||||||
|
endpoint = "/themes/$INVALID_PK",
|
||||||
|
expectedErrorCode = ThemeErrorCode.THEME_NOT_FOUND
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context("관리자 페이지에서 특정 테마의 상세 정보를 조회한다.") {
|
context("관리자 페이지에서 특정 테마의 상세 정보를 조회한다.") {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user