generated from pricelees/issue-pr-template
[#56] 예약 & 결제 프로세스 및 패키지 구조 재정의 #57
@ -0,0 +1,134 @@
|
|||||||
|
package com.sangdol.roomescape.schedule.business
|
||||||
|
|
||||||
|
import com.sangdol.common.persistence.IDGenerator
|
||||||
|
import com.sangdol.common.utils.KoreaDate
|
||||||
|
import com.sangdol.roomescape.admin.business.AdminService
|
||||||
|
import com.sangdol.roomescape.common.types.AuditingInfo
|
||||||
|
import com.sangdol.roomescape.common.types.Auditor
|
||||||
|
import com.sangdol.roomescape.schedule.business.domain.ScheduleOverview
|
||||||
|
import com.sangdol.roomescape.schedule.dto.AdminScheduleSummaryListResponse
|
||||||
|
import com.sangdol.roomescape.schedule.dto.ScheduleCreateRequest
|
||||||
|
import com.sangdol.roomescape.schedule.dto.ScheduleCreateResponse
|
||||||
|
import com.sangdol.roomescape.schedule.dto.ScheduleUpdateRequest
|
||||||
|
import com.sangdol.roomescape.schedule.exception.ScheduleErrorCode
|
||||||
|
import com.sangdol.roomescape.schedule.exception.ScheduleException
|
||||||
|
import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleEntity
|
||||||
|
import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleEntityFactory
|
||||||
|
import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleRepository
|
||||||
|
import com.sangdol.roomescape.schedule.mapper.toAdminSummaryListResponse
|
||||||
|
import io.github.oshai.kotlinlogging.KLogger
|
||||||
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
|
import org.springframework.data.repository.findByIdOrNull
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
import java.time.LocalDate
|
||||||
|
|
||||||
|
private val log: KLogger = KotlinLogging.logger {}
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class AdminScheduleService(
|
||||||
|
private val scheduleRepository: ScheduleRepository,
|
||||||
|
private val scheduleValidator: ScheduleValidator,
|
||||||
|
private val idGenerator: IDGenerator,
|
||||||
|
private val adminService: AdminService
|
||||||
|
) {
|
||||||
|
// ========================================
|
||||||
|
// All-Admin (본사, 매장 모두 사용가능)
|
||||||
|
// ========================================
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
fun searchSchedules(storeId: Long, date: LocalDate?, themeId: Long?): AdminScheduleSummaryListResponse {
|
||||||
|
log.info { "[searchSchedules] 일정 검색 시작: storeId=$storeId, date=$date, themeId=$themeId" }
|
||||||
|
|
||||||
|
val searchDate = date ?: KoreaDate.today()
|
||||||
|
|
||||||
|
val schedules: List<ScheduleOverview> =
|
||||||
|
scheduleRepository.findStoreSchedulesWithThemeByDate(storeId, searchDate)
|
||||||
|
.filter { (themeId == null) || (it.themeId == themeId) }
|
||||||
|
.sortedBy { it.time }
|
||||||
|
|
||||||
|
return schedules.toAdminSummaryListResponse()
|
||||||
|
.also {
|
||||||
|
log.info { "[searchSchedules] ${it.schedules.size} 개의 일정 조회 완료" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
fun findScheduleAudit(id: Long): AuditingInfo {
|
||||||
|
log.info { "[findDetail] 일정 감사 정보 조회 시작: id=$id" }
|
||||||
|
|
||||||
|
val schedule: ScheduleEntity = findOrThrow(id)
|
||||||
|
|
||||||
|
val createdBy: Auditor = adminService.findOperatorOrUnknown(schedule.createdBy)
|
||||||
|
val updatedBy: Auditor = adminService.findOperatorOrUnknown(schedule.updatedBy)
|
||||||
|
|
||||||
|
return AuditingInfo(schedule.createdAt, createdBy, schedule.updatedAt, updatedBy)
|
||||||
|
.also { log.info { "[findDetail] 일정 감사 정보 조회 완료: id=$id" } }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// Store-Admin (매장 관리자 로그인 필요)
|
||||||
|
// ========================================
|
||||||
|
@Transactional
|
||||||
|
fun createSchedule(storeId: Long, request: ScheduleCreateRequest): ScheduleCreateResponse {
|
||||||
|
log.info { "[createSchedule] 일정 생성 시작: storeId=${storeId}, date=${request.date}, time=${request.time}, themeId=${request.themeId}" }
|
||||||
|
|
||||||
|
scheduleValidator.validateCanCreate(storeId, request)
|
||||||
|
|
||||||
|
val schedule = ScheduleEntityFactory.create(
|
||||||
|
id = idGenerator.create(),
|
||||||
|
date = request.date,
|
||||||
|
time = request.time,
|
||||||
|
storeId = storeId,
|
||||||
|
themeId = request.themeId
|
||||||
|
).also {
|
||||||
|
scheduleRepository.save(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ScheduleCreateResponse(schedule.id)
|
||||||
|
.also {
|
||||||
|
log.info { "[createSchedule] 일정 생성 완료: id=${it.id}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
fun updateSchedule(id: Long, request: ScheduleUpdateRequest) {
|
||||||
|
log.info { "[updateSchedule] 일정 수정 시작: id=$id, request=${request}" }
|
||||||
|
|
||||||
|
if (request.isAllParamsNull()) {
|
||||||
|
log.info { "[updateSchedule] 일정 변경 사항 없음: id=$id" }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val schedule: ScheduleEntity = findOrThrow(id).also {
|
||||||
|
scheduleValidator.validateCanUpdate(it, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
schedule.modifyIfNotNull(request.time, request.status).also {
|
||||||
|
log.info { "[updateSchedule] 일정 수정 완료: id=$id, request=${request}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
fun deleteSchedule(id: Long) {
|
||||||
|
log.info { "[deleteSchedule] 일정 삭제 시작: id=$id" }
|
||||||
|
|
||||||
|
val schedule: ScheduleEntity = findOrThrow(id).also {
|
||||||
|
scheduleValidator.validateCanDelete(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduleRepository.delete(schedule).also {
|
||||||
|
log.info { "[deleteSchedule] 일정 삭제 완료: id=$id" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findOrThrow(id: Long): ScheduleEntity {
|
||||||
|
log.info { "[findOrThrow] 일정 조회 시작: id=$id" }
|
||||||
|
|
||||||
|
return scheduleRepository.findByIdOrNull(id)
|
||||||
|
?.also { log.info { "[findOrThrow] 일정 조회 완료: id=$id" } }
|
||||||
|
?: run {
|
||||||
|
log.warn { "[findOrThrow] 일정 조회 실패. id=$id" }
|
||||||
|
throw ScheduleException(ScheduleErrorCode.SCHEDULE_NOT_FOUND)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -34,25 +34,11 @@ import java.time.LocalTime
|
|||||||
|
|
||||||
private val log: KLogger = KotlinLogging.logger {}
|
private val log: KLogger = KotlinLogging.logger {}
|
||||||
|
|
||||||
/**
|
|
||||||
* Structure:
|
|
||||||
* - Public: 모두가 접근 가능
|
|
||||||
* - User: 회원(로그인된 사용자)가 사용 가능
|
|
||||||
* - All-Admin: 모든 관리자가 사용 가능
|
|
||||||
* - Store-Admin: 매장 관리자만 사용 가능
|
|
||||||
* - Other-Service: 다른 서비스에서 호출하는 메서드
|
|
||||||
* - Common: 공통 메서드
|
|
||||||
*/
|
|
||||||
@Service
|
@Service
|
||||||
class ScheduleService(
|
class ScheduleService(
|
||||||
private val scheduleRepository: ScheduleRepository,
|
private val scheduleRepository: ScheduleRepository,
|
||||||
private val scheduleValidator: ScheduleValidator,
|
private val scheduleValidator: ScheduleValidator
|
||||||
private val idGenerator: IDGenerator,
|
|
||||||
private val adminService: AdminService
|
|
||||||
) {
|
) {
|
||||||
// ========================================
|
|
||||||
// Public (인증 불필요)
|
|
||||||
// ========================================
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
fun getStoreScheduleByDate(storeId: Long, date: LocalDate): ScheduleWithThemeListResponse {
|
fun getStoreScheduleByDate(storeId: Long, date: LocalDate): ScheduleWithThemeListResponse {
|
||||||
log.info { "[getStoreScheduleByDate] 매장 일정 조회: storeId=${storeId}, date=$date" }
|
log.info { "[getStoreScheduleByDate] 매장 일정 조회: storeId=${storeId}, date=$date" }
|
||||||
@ -75,9 +61,6 @@ class ScheduleService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================
|
|
||||||
// User (회원 로그인 필요)
|
|
||||||
// ========================================
|
|
||||||
@Transactional
|
@Transactional
|
||||||
fun holdSchedule(id: Long) {
|
fun holdSchedule(id: Long) {
|
||||||
log.info { "[holdSchedule] 일정 Holding 시작: id=$id" }
|
log.info { "[holdSchedule] 일정 Holding 시작: id=$id" }
|
||||||
@ -95,98 +78,6 @@ class ScheduleService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================
|
|
||||||
// All-Admin (본사, 매장 모두 사용가능)
|
|
||||||
// ========================================
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
fun searchSchedules(storeId: Long, date: LocalDate?, themeId: Long?): AdminScheduleSummaryListResponse {
|
|
||||||
log.info { "[searchSchedules] 일정 검색 시작: storeId=$storeId, date=$date, themeId=$themeId" }
|
|
||||||
|
|
||||||
val searchDate = date ?: KoreaDate.today()
|
|
||||||
|
|
||||||
val schedules: List<ScheduleOverview> =
|
|
||||||
scheduleRepository.findStoreSchedulesWithThemeByDate(storeId, searchDate)
|
|
||||||
.filter { (themeId == null) || (it.themeId == themeId) }
|
|
||||||
.sortedBy { it.time }
|
|
||||||
|
|
||||||
return schedules.toAdminSummaryListResponse()
|
|
||||||
.also {
|
|
||||||
log.info { "[searchSchedules] ${it.schedules.size} 개의 일정 조회 완료" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
fun findScheduleAudit(id: Long): AuditingInfo {
|
|
||||||
log.info { "[findDetail] 일정 감사 정보 조회 시작: id=$id" }
|
|
||||||
|
|
||||||
val schedule: ScheduleEntity = findOrThrow(id)
|
|
||||||
|
|
||||||
val createdBy: Auditor = adminService.findOperatorOrUnknown(schedule.createdBy)
|
|
||||||
val updatedBy: Auditor = adminService.findOperatorOrUnknown(schedule.updatedBy)
|
|
||||||
|
|
||||||
return AuditingInfo(schedule.createdAt, createdBy, schedule.updatedAt, updatedBy)
|
|
||||||
.also { log.info { "[findDetail] 일정 감사 정보 조회 완료: id=$id" } }
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========================================
|
|
||||||
// Store-Admin (매장 관리자 로그인 필요)
|
|
||||||
// ========================================
|
|
||||||
@Transactional
|
|
||||||
fun createSchedule(storeId: Long, request: ScheduleCreateRequest): ScheduleCreateResponse {
|
|
||||||
log.info { "[createSchedule] 일정 생성 시작: storeId=${storeId}, date=${request.date}, time=${request.time}, themeId=${request.themeId}" }
|
|
||||||
|
|
||||||
scheduleValidator.validateCanCreate(storeId, request)
|
|
||||||
|
|
||||||
val schedule = ScheduleEntityFactory.create(
|
|
||||||
id = idGenerator.create(),
|
|
||||||
date = request.date,
|
|
||||||
time = request.time,
|
|
||||||
storeId = storeId,
|
|
||||||
themeId = request.themeId
|
|
||||||
).also {
|
|
||||||
scheduleRepository.save(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ScheduleCreateResponse(schedule.id)
|
|
||||||
.also {
|
|
||||||
log.info { "[createSchedule] 일정 생성 완료: id=${it.id}" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional
|
|
||||||
fun updateSchedule(id: Long, request: ScheduleUpdateRequest) {
|
|
||||||
log.info { "[updateSchedule] 일정 수정 시작: id=$id, request=${request}" }
|
|
||||||
|
|
||||||
if (request.isAllParamsNull()) {
|
|
||||||
log.info { "[updateSchedule] 일정 변경 사항 없음: id=$id" }
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val schedule: ScheduleEntity = findOrThrow(id).also {
|
|
||||||
scheduleValidator.validateCanUpdate(it, request)
|
|
||||||
}
|
|
||||||
|
|
||||||
schedule.modifyIfNotNull(request.time, request.status).also {
|
|
||||||
log.info { "[updateSchedule] 일정 수정 완료: id=$id, request=${request}" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional
|
|
||||||
fun deleteSchedule(id: Long) {
|
|
||||||
log.info { "[deleteSchedule] 일정 삭제 시작: id=$id" }
|
|
||||||
|
|
||||||
val schedule: ScheduleEntity = findOrThrow(id).also {
|
|
||||||
scheduleValidator.validateCanDelete(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
scheduleRepository.delete(schedule).also {
|
|
||||||
log.info { "[deleteSchedule] 일정 삭제 완료: id=$id" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========================================
|
|
||||||
// Other-Service (API 없이 다른 서비스에서 호출)
|
|
||||||
// ========================================
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
fun findSummaryWithLock(id: Long): ScheduleSummaryResponse {
|
fun findSummaryWithLock(id: Long): ScheduleSummaryResponse {
|
||||||
log.info { "[findDateTimeById] 일정 개요 조회 시작 : id=$id" }
|
log.info { "[findDateTimeById] 일정 개요 조회 시작 : id=$id" }
|
||||||
@ -222,20 +113,6 @@ class ScheduleService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================
|
|
||||||
// Common (공통 메서드)
|
|
||||||
// ========================================
|
|
||||||
private fun findOrThrow(id: Long): ScheduleEntity {
|
|
||||||
log.info { "[findOrThrow] 일정 조회 시작: id=$id" }
|
|
||||||
|
|
||||||
return scheduleRepository.findByIdOrNull(id)
|
|
||||||
?.also { log.info { "[findOrThrow] 일정 조회 완료: id=$id" } }
|
|
||||||
?: run {
|
|
||||||
log.warn { "[findOrThrow] 일정 조회 실패. id=$id" }
|
|
||||||
throw ScheduleException(ScheduleErrorCode.SCHEDULE_NOT_FOUND)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findForUpdateOrThrow(id: Long): ScheduleEntity {
|
private fun findForUpdateOrThrow(id: Long): ScheduleEntity {
|
||||||
log.info { "[findForUpdateOrThrow] 일정 LOCK + 조회 시작: id=$id" }
|
log.info { "[findForUpdateOrThrow] 일정 LOCK + 조회 시작: id=$id" }
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package com.sangdol.roomescape.schedule.web
|
|||||||
|
|
||||||
import com.sangdol.common.types.web.CommonApiResponse
|
import com.sangdol.common.types.web.CommonApiResponse
|
||||||
import com.sangdol.roomescape.common.types.AuditingInfo
|
import com.sangdol.roomescape.common.types.AuditingInfo
|
||||||
|
import com.sangdol.roomescape.schedule.business.AdminScheduleService
|
||||||
import com.sangdol.roomescape.schedule.business.ScheduleService
|
import com.sangdol.roomescape.schedule.business.ScheduleService
|
||||||
import com.sangdol.roomescape.schedule.docs.AdminScheduleAPI
|
import com.sangdol.roomescape.schedule.docs.AdminScheduleAPI
|
||||||
import com.sangdol.roomescape.schedule.dto.AdminScheduleSummaryListResponse
|
import com.sangdol.roomescape.schedule.dto.AdminScheduleSummaryListResponse
|
||||||
@ -17,7 +18,7 @@ import java.time.LocalDate
|
|||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/admin")
|
@RequestMapping("/admin")
|
||||||
class AdminScheduleController(
|
class AdminScheduleController(
|
||||||
private val scheduleService: ScheduleService,
|
private val adminScheduleService: AdminScheduleService,
|
||||||
) : AdminScheduleAPI {
|
) : AdminScheduleAPI {
|
||||||
@GetMapping("/stores/{storeId}/schedules")
|
@GetMapping("/stores/{storeId}/schedules")
|
||||||
override fun searchSchedules(
|
override fun searchSchedules(
|
||||||
@ -25,7 +26,7 @@ class AdminScheduleController(
|
|||||||
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") date: LocalDate?,
|
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") date: LocalDate?,
|
||||||
@RequestParam(required = false) themeId: Long?,
|
@RequestParam(required = false) themeId: Long?,
|
||||||
): ResponseEntity<CommonApiResponse<AdminScheduleSummaryListResponse>> {
|
): ResponseEntity<CommonApiResponse<AdminScheduleSummaryListResponse>> {
|
||||||
val response = scheduleService.searchSchedules(storeId, date, themeId)
|
val response = adminScheduleService.searchSchedules(storeId, date, themeId)
|
||||||
|
|
||||||
return ResponseEntity.ok(CommonApiResponse(response))
|
return ResponseEntity.ok(CommonApiResponse(response))
|
||||||
}
|
}
|
||||||
@ -34,7 +35,7 @@ class AdminScheduleController(
|
|||||||
override fun findScheduleAudit(
|
override fun findScheduleAudit(
|
||||||
@PathVariable("id") id: Long
|
@PathVariable("id") id: Long
|
||||||
): ResponseEntity<CommonApiResponse<AuditingInfo>> {
|
): ResponseEntity<CommonApiResponse<AuditingInfo>> {
|
||||||
val response = scheduleService.findScheduleAudit(id)
|
val response = adminScheduleService.findScheduleAudit(id)
|
||||||
|
|
||||||
return ResponseEntity.ok(CommonApiResponse(response))
|
return ResponseEntity.ok(CommonApiResponse(response))
|
||||||
}
|
}
|
||||||
@ -44,7 +45,7 @@ class AdminScheduleController(
|
|||||||
@PathVariable("storeId") storeId: Long,
|
@PathVariable("storeId") storeId: Long,
|
||||||
@Valid @RequestBody request: ScheduleCreateRequest
|
@Valid @RequestBody request: ScheduleCreateRequest
|
||||||
): ResponseEntity<CommonApiResponse<ScheduleCreateResponse>> {
|
): ResponseEntity<CommonApiResponse<ScheduleCreateResponse>> {
|
||||||
val response = scheduleService.createSchedule(storeId, request)
|
val response = adminScheduleService.createSchedule(storeId, request)
|
||||||
|
|
||||||
return ResponseEntity.ok(CommonApiResponse(response))
|
return ResponseEntity.ok(CommonApiResponse(response))
|
||||||
}
|
}
|
||||||
@ -54,7 +55,7 @@ class AdminScheduleController(
|
|||||||
@PathVariable("id") id: Long,
|
@PathVariable("id") id: Long,
|
||||||
@Valid @RequestBody request: ScheduleUpdateRequest
|
@Valid @RequestBody request: ScheduleUpdateRequest
|
||||||
): ResponseEntity<CommonApiResponse<Unit>> {
|
): ResponseEntity<CommonApiResponse<Unit>> {
|
||||||
scheduleService.updateSchedule(id, request)
|
adminScheduleService.updateSchedule(id, request)
|
||||||
|
|
||||||
return ResponseEntity.ok(CommonApiResponse(Unit))
|
return ResponseEntity.ok(CommonApiResponse(Unit))
|
||||||
}
|
}
|
||||||
@ -63,7 +64,7 @@ class AdminScheduleController(
|
|||||||
override fun deleteSchedule(
|
override fun deleteSchedule(
|
||||||
@PathVariable("id") id: Long
|
@PathVariable("id") id: Long
|
||||||
): ResponseEntity<CommonApiResponse<Unit>> {
|
): ResponseEntity<CommonApiResponse<Unit>> {
|
||||||
scheduleService.deleteSchedule(id)
|
adminScheduleService.deleteSchedule(id)
|
||||||
|
|
||||||
return ResponseEntity.noContent().build()
|
return ResponseEntity.noContent().build()
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user