pricelees 258b5f042d [#39] '시간' -> '일정' 스키마 변경으로 테마별 시간 지정 (#40)
<!-- 제목 양식 -->
<!-- [이슈번호] 작업 요약 (예시: [#10] Gitea 템플릿 생성) -->

## 📝 관련 이슈 및 PR

**PR과 관련된 이슈 번호**
- #39

##  작업 내용
<!-- 어떤 작업을 했는지 알려주세요! -->
- 기존 시간 테이블은 유지하고 일정 스키마 및 기능 도입
- 관련 프론트엔드 페이지 생성

## 🧪 테스트
<!-- 어떤 테스트를 생각했고 진행했는지 알려주세요! -->
- 이전 테마 작업과 마찬가지로 모든 API를 대상으로 통합 테스트 진행

## 📚 참고 자료 및 기타
<!-- 참고한 자료, 또는 논의할 사항이 있다면 알려주세요! -->

Reviewed-on: #40
Co-authored-by: pricelees <priceelees@gmail.com>
Co-committed-by: pricelees <priceelees@gmail.com>
2025-09-04 04:14:12 +00:00

131 lines
5.0 KiB
Kotlin

package roomescape.schedule.business
import ScheduleException
import com.github.f4b6a3.tsid.TsidFactory
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 roomescape.common.config.next
import roomescape.member.business.MemberService
import roomescape.schedule.exception.ScheduleErrorCode
import roomescape.schedule.infrastructure.persistence.ScheduleEntity
import roomescape.schedule.infrastructure.persistence.ScheduleRepository
import roomescape.schedule.infrastructure.persistence.ScheduleStatus
import roomescape.schedule.web.*
import java.time.LocalDate
private val log: KLogger = KotlinLogging.logger {}
@Service
class ScheduleService(
private val scheduleRepository: ScheduleRepository,
private val scheduleValidator: ScheduleValidator,
private val tsidFactory: TsidFactory,
private val memberService: MemberService
) {
@Transactional(readOnly = true)
fun findThemesByDate(date: LocalDate): AvailableThemeIdListResponse {
log.info { "[ScheduleService.findThemesByDate] 동일한 날짜의 모든 테마 조회: date=$date" }
return scheduleRepository.findAllByDate(date)
.toThemeIdListResponse()
.also {
log.info { "[ScheduleService.findThemesByDate] date=${date}${it.themeIds.size}개 테마 조회 완료" }
}
}
@Transactional(readOnly = true)
fun findSchedules(date: LocalDate, themeId: Long): ScheduleRetrieveListResponse {
log.info { "[ScheduleService.findSchedules] 동일한 날짜와 테마인 모든 일정 조회: date=${date}, themeId=${themeId}" }
return scheduleRepository.findAllByDateAndThemeId(date, themeId)
.toRetrieveListResponse()
.also {
log.info { "[ScheduleService.findSchedules] date=${date}, themeId=${themeId}${it.schedules.size}개 일정 조회 완료" }
}
}
@Transactional(readOnly = true)
fun findDetail(id: Long): ScheduleDetailRetrieveResponse {
log.info { "[ScheduleService.findDetail] 일정 상세 정보조회 시작: id=$id" }
val schedule: ScheduleEntity = findOrThrow(id)
val createdBy = memberService.findById(schedule.createdBy).name
val updatedBy = memberService.findById(schedule.updatedBy).name
return schedule.toDetailRetrieveResponse(createdBy, updatedBy)
.also {
log.info { "[ScheduleService.findDetail] 일정 상세 조회 완료: id=$id" }
}
}
@Transactional
fun createSchedule(request: ScheduleCreateRequest): ScheduleCreateResponse {
log.info { "[ScheduleService.createSchedule] 일정 생성 시작: date=${request.date}, time=${request.time}, themeId=${request.themeId}" }
scheduleValidator.validateCanCreate(request)
val schedule = ScheduleEntity(
id = tsidFactory.next(),
date = request.date,
time = request.time,
themeId = request.themeId,
status = ScheduleStatus.AVAILABLE
)
return ScheduleCreateResponse(scheduleRepository.save(schedule).id)
.also {
log.info { "[ScheduleService.createSchedule] 일정 생성 완료: id=${it.id}" }
}
}
@Transactional
fun updateSchedule(id: Long, request: ScheduleUpdateRequest) {
log.info { "[ScheduleService.updateSchedule] 일정 수정 시작: id=$id, request=${request}" }
if (request.isAllParamsNull()) {
log.info { "[ScheduleService.updateSchedule] 일정 변경 사항 없음: id=$id" }
return
}
val schedule: ScheduleEntity = findOrThrow(id)
scheduleValidator.validateCanUpdate(schedule, request)
schedule.modifyIfNotNull(
request.time,
request.status
).also {
log.info { "[ScheduleService.updateSchedule] 일정 수정 완료: id=$id, request=${request}" }
}
}
@Transactional
fun deleteSchedule(id: Long) {
log.info { "[ScheduleService.deleteSchedule] 일정 삭제 시작: id=$id" }
val schedule: ScheduleEntity = findOrThrow(id)
scheduleValidator.validateCanDelete(schedule)
scheduleRepository.delete(schedule).also {
log.info { "[ScheduleService.deleteSchedule] 일정 삭제 완료: id=$id" }
}
}
private fun findOrThrow(id: Long): ScheduleEntity {
log.info { "[ScheduleService.findOrThrow] 일정 조회 시작: id=$id" }
return scheduleRepository.findByIdOrNull(id)
?.also { log.info { "[ScheduleService.findOrThrow] 일정 조회 완료: id=$id" } }
?: run {
log.warn { "[ScheduleService.updateSchedule] 일정 조회 실패. id=$id" }
throw ScheduleException(ScheduleErrorCode.SCHEDULE_NOT_FOUND)
}
}
}