diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/in/web/AdminWebController.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/in/web/AdminWebController.kt new file mode 100644 index 0000000..262ffad --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/in/web/AdminWebController.kt @@ -0,0 +1,73 @@ +package hs.kr.entrydsm.status.domain.status.adapter.`in`.web + +import hs.kr.entrydsm.status.domain.status.application.port.`in`.AnnounceResultUseCase +import hs.kr.entrydsm.status.domain.status.application.port.`in`.CancelApplicationSubmitUseCase +import hs.kr.entrydsm.status.domain.status.application.port.`in`.StartScreeningUseCase +import hs.kr.entrydsm.status.domain.status.application.port.`in`.UpdateIsPrintsArrivedUseCase +import org.springframework.web.bind.annotation.PatchMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +/** + * 관리자용 상태 관리 REST API 컨트롤러입니다. + * 관리자가 지원자의 상태를 직접 변경할 수 있는 기능을 제공합니다. + * + * @property updateIsPrintsArrivedUseCase 서류 도착 상태 변경 유스케이스 + * @property cancelApplicationSubmitUseCase 지원서 제출 취소 유스케이스 + * @property startScreeningUseCase 전형 시작 유스케이스 + * @property announceResultUseCase 합격 발표 유스케이스 + */ +@RestController +@RequestMapping("/admin/status") +class AdminWebController( + private val updateIsPrintsArrivedUseCase: UpdateIsPrintsArrivedUseCase, + private val cancelApplicationSubmitUseCase: CancelApplicationSubmitUseCase, + private val startScreeningUseCase: StartScreeningUseCase, + private val announceResultUseCase: AnnounceResultUseCase +) { + + /** + * 지원서 제출을 취소합니다. + * 제출된 지원서를 작성 중 상태로 되돌립니다. + * + * @param receiptCode 접수번호 + */ + @PatchMapping("/submitted/{receipt-code}") + fun cancelApplicationSubmit(@PathVariable("receipt-code") receiptCode: Long) { + cancelApplicationSubmitUseCase.execute(receiptCode) + } + + /** + * 서류 도착을 확인합니다. + * 등기우편으로 제출된 서류의 도착을 관리자가 확인하여 상태를 변경합니다. + * + * @param receiptCode 접수번호 + */ + @PatchMapping("/prints-arrived/{receipt-code}") + fun updateIsPrintsArrivedService(@PathVariable("receipt-code") receiptCode: Long) { + updateIsPrintsArrivedUseCase.execute(receiptCode) + } + + /** + * 전형을 시작합니다. + * 서류 검토가 완료된 후 1차 또는 2차 전형을 시작합니다. + * + * @param receiptCode 접수번호 + */ + @PatchMapping("/screening/{receipt-code}") + fun startScreening(@PathVariable("receipt-code") receiptCode: Long) { + startScreeningUseCase.execute(receiptCode) + } + + /** + * 합격 결과를 발표합니다. + * 최종 전형 결과를 발표하고 상태를 변경합니다. + * + * @param receiptCode 접수번호 + */ + @PatchMapping("/announce/{receipt-code}") + fun announceResult(@PathVariable("receipt-code") receiptCode: Long) { + announceResultUseCase.execute(receiptCode) + } +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/in/web/InternalStatusWebController.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/in/web/InternalStatusWebController.kt new file mode 100644 index 0000000..ff2377a --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/in/web/InternalStatusWebController.kt @@ -0,0 +1,61 @@ +package hs.kr.entrydsm.status.domain.status.adapter.`in`.web + +import hs.kr.entrydsm.status.domain.status.application.port.`in`.GetAllStatusUseCase +import hs.kr.entrydsm.status.domain.status.application.port.`in`.GetStatusByReceiptCodeUseCase +import hs.kr.entrydsm.status.infrastructure.grpc.server.dto.response.InternalStatusResponse +import hs.kr.entrydsm.status.domain.status.application.port.`in`.UpdateExamCodeUseCase +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PutMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +/** + * 내부 시스템용 상태 조회 REST API 컨트롤러입니다. + * 다른 마이크로서비스에서 지원자의 상태 정보를 조회할 수 있는 기능을 제공합니다. + * + * @property getStatusByReceiptCodeUseCase 접수번호별 상태 조회 유스케이스 + * @property getAllStatusUseCase 전체 상태 조회 유스케이스 + * @property updateExamCodeUseCase 수험번호 업데이트 유스케이스 + */ +@RestController +@RequestMapping("/internal/status") +class InternalStatusWebController( + private val getStatusByReceiptCodeUseCase: GetStatusByReceiptCodeUseCase, + private val getAllStatusUseCase: GetAllStatusUseCase, + private val updateExamCodeUseCase: UpdateExamCodeUseCase +) { + + /** + * 접수번호로 상태를 조회합니다. + * + * @param receiptCode 접수번호 + * @return 지원자의 상태 정보 + */ + @GetMapping("/{receipt-code}") + fun getStatusByReceiptCode(@PathVariable("receipt-code") receiptCode: Long): InternalStatusResponse { + return getStatusByReceiptCodeUseCase.execute(receiptCode) + } + + /** + * 모든 지원자의 상태 목록을 조회합니다. + * + * @return 전체 지원자 상태 정보 목록 + */ + @GetMapping("/list") + fun getAllStatus(): List { + return getAllStatusUseCase.execute() + } + + /** + * 수험번호를 업데이트합니다. + * + * @param receiptCode 접수번호 + * @param examCode 새로운 수험번호 + */ + @PutMapping("/{receipt-code}") + fun updateExamCode(@PathVariable("receipt-code") receiptCode: Long, @RequestParam examCode: String) { + updateExamCodeUseCase.execute(receiptCode, examCode) + } +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/out/domain/StatusCache.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/out/domain/StatusCache.kt new file mode 100644 index 0000000..93cdea8 --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/out/domain/StatusCache.kt @@ -0,0 +1,54 @@ +package hs.kr.entrydsm.status.domain.status.adapter.out.domain + +import hs.kr.entrydsm.status.domain.status.model.ApplicationStatus +import hs.kr.entrydsm.status.domain.status.model.Status +import org.springframework.data.annotation.Id +import org.springframework.data.redis.core.RedisHash +import org.springframework.data.redis.core.TimeToLive + +/** + * Redis 캐시용 상태 엔티티 클래스입니다. + * 상태 조회 성능 향상을 위한 캐시 데이터를 관리합니다. + * + * @property receiptCode 접수번호 (Redis 키로 사용) + * @property applicationStatus 지원 상태 + * @property examCode 수험번호 + * @property isFirstRoundPass 1차 전형 합격 여부 + * @property isSecondRoundPass 2차 전형 합격 여부 + * @property ttl Time To Live (캐시 만료 시간, 초 단위) + */ +@RedisHash(value = "status_cache") +class StatusCache( + @Id + val receiptCode: Long, + + val applicationStatus: ApplicationStatus, + + val examCode: String?, + + val isFirstRoundPass: Boolean, + + val isSecondRoundPass: Boolean, + + @TimeToLive + var ttl: Long +){ + companion object{ + /** + * Status 도메인 모델로부터 StatusCache 인스턴스를 생성합니다. + * + * @param status 도메인 모델 Status 인스턴스 + * @return StatusCache 인스턴스 (TTL 10분으로 설정) + */ + fun from(status: Status): StatusCache { + return StatusCache( + receiptCode = status.receiptCode, + applicationStatus = status.applicationStatus, + examCode = status.examCode, + isFirstRoundPass = status.isFirstRoundPass, + isSecondRoundPass = status.isSecondRoundPass, + ttl = 60 * 10 + ) + } + } +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/out/domain/StatusJpaEntity.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/out/domain/StatusJpaEntity.kt new file mode 100644 index 0000000..42645b7 --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/out/domain/StatusJpaEntity.kt @@ -0,0 +1,38 @@ +package hs.kr.entrydsm.status.domain.status.adapter.out.domain + +import hs.kr.entrydsm.status.domain.status.model.ApplicationStatus +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.EnumType +import jakarta.persistence.Enumerated +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id + +/** + * 지원 상태 정보를 저장하는 JPA 엔티티 클래스입니다. + * 데이터베이스의 status 테이블과 매핑되며, 지원자의 전형 진행 상태를 관리합니다. + * + * @property applicationStatus 현재 지원 상태 + * @property examCode 수험번호 (고유값) + * @property isFirstRoundPass 1차 전형 합격 여부 + * @property isSecondRoundPass 2차 전형 합격 여부 + * @property receiptCode 접수번호 + * @property id 데이터베이스 기본키 (자동 생성) + */ +@Entity(name = "status") +class StatusJpaEntity( + @Enumerated(EnumType.STRING) + @Column(name = "application_status") + var applicationStatus: ApplicationStatus = ApplicationStatus.NOT_APPLIED, + @Column(unique = true) + var examCode: String? = null, + var isFirstRoundPass: Boolean = false, + var isSecondRoundPass: Boolean = false, + val receiptCode: Long +) { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long = 0 + +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/out/mapper/StatusMapper.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/out/mapper/StatusMapper.kt new file mode 100644 index 0000000..9d71e2a --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/out/mapper/StatusMapper.kt @@ -0,0 +1,39 @@ +package hs.kr.entrydsm.status.domain.status.adapter.out.mapper + +import hs.kr.entrydsm.status.domain.status.adapter.out.domain.StatusJpaEntity +import hs.kr.entrydsm.status.domain.status.model.Status +import hs.kr.entrydsm.status.global.mapper.GenericMapper +import org.mapstruct.Mapper + +/** + * Status 도메인 모델과 StatusJpaEntity 간의 변환을 담당하는 매퍼 클래스입니다. + * MapStruct를 사용하여 도메인 계층과 인프라스트럭처 계층 간의 데이터 변환을 처리합니다. + */ +@Mapper(componentModel = "spring") +abstract class StatusMapper: GenericMapper { + + /** + * Status 도메인 모델을 StatusJpaEntity로 변환합니다. + * + * @param model 변환할 Status 도메인 모델 + * @return 변환된 StatusJpaEntity + */ + abstract override fun toEntity(model: Status): StatusJpaEntity + + /** + * StatusJpaEntity를 Status 도메인 모델로 변환합니다. + * + * @param entity 변환할 StatusJpaEntity (null 가능) + * @return 변환된 Status 도메인 모델 (null 가능) + */ + abstract override fun toModel(entity: StatusJpaEntity?): Status? + + /** + * StatusJpaEntity를 Status 도메인 모델로 변환합니다. + * null이 아닌 entity를 받아 null이 아닌 모델을 반환합니다. + * + * @param entity 변환할 StatusJpaEntity (null이 아님) + * @return 변환된 Status 도메인 모델 (null이 아님) + */ + abstract override fun toModelNotNull(entity: StatusJpaEntity): Status +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/out/persistence/StatusPersistenceAdapter.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/out/persistence/StatusPersistenceAdapter.kt new file mode 100644 index 0000000..16190e1 --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/out/persistence/StatusPersistenceAdapter.kt @@ -0,0 +1,58 @@ +package hs.kr.entrydsm.status.domain.status.adapter.out.persistence + +import hs.kr.entrydsm.status.domain.status.adapter.out.mapper.StatusMapper +import hs.kr.entrydsm.status.domain.status.adapter.out.persistence.repository.StatusRepository +import hs.kr.entrydsm.status.domain.status.application.port.out.StatusPort +import hs.kr.entrydsm.status.domain.status.model.Status +import org.springframework.stereotype.Component + +/** + * 상태 데이터의 영속성 처리를 담당하는 어댑터 클래스입니다. + * 헥사고날 아키텍처의 Driven Adapter 역할을 하며, 도메인 계층의 StatusPort를 구현합니다. + * + * @property statusRepository JPA 기반 상태 데이터 저장소 + * @property statusMapper 도메인 모델과 JPA 엔티티 간 변환 매퍼 + */ +@Component +class StatusPersistenceAdapter( + private val statusRepository: StatusRepository, + private val statusMapper: StatusMapper, +): StatusPort { + + /** + * 상태 정보를 저장합니다. + * + * @param status 저장할 상태 도메인 모델 + */ + override fun save(status: Status) { + statusRepository.save(statusMapper.toEntity(status)) + } + + /** + * 접수번호로 상태 정보를 조회합니다. + * + * @param receiptCode 조회할 접수번호 + * @return 조회된 상태 도메인 모델 (없으면 null) + */ + override fun findByReceiptCode(receiptCode: Long): Status? = + statusRepository.findByReceiptCode(receiptCode)?.let { statusMapper.toModel(it) } + + /** + * 모든 상태 정보를 조회합니다. + * + * @return 모든 상태 도메인 모델 리스트 + */ + override fun findAll(): List { + return statusRepository.findAll() + .map { statusMapper.toModelNotNull(it) } + } + + /** + * 접수번호로 상태 정보를 삭제합니다. + * + * @param receiptCode 삭제할 접수번호 + */ + override fun deleteByReceiptCode(receiptCode: Long) { + statusRepository.deleteByReceiptCode(receiptCode) + } +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/out/persistence/repository/StatusCacheRepository.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/out/persistence/repository/StatusCacheRepository.kt new file mode 100644 index 0000000..9aa55f3 --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/out/persistence/repository/StatusCacheRepository.kt @@ -0,0 +1,10 @@ +package hs.kr.entrydsm.status.domain.status.adapter.out.persistence.repository + +import hs.kr.entrydsm.status.domain.status.adapter.out.domain.StatusCache +import org.springframework.data.repository.CrudRepository + +/** + * 상태 캐시를 위한 Redis 저장소 인터페이스입니다. + * Spring Data Redis를 통해 상태 정보의 캐시 관리를 담당합니다. + */ +interface StatusCacheRepository : CrudRepository diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/out/persistence/repository/StatusRepository.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/out/persistence/repository/StatusRepository.kt new file mode 100644 index 0000000..0ea7503 --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/adapter/out/persistence/repository/StatusRepository.kt @@ -0,0 +1,26 @@ +package hs.kr.entrydsm.status.domain.status.adapter.out.persistence.repository + +import hs.kr.entrydsm.status.domain.status.adapter.out.domain.StatusJpaEntity +import org.springframework.data.repository.CrudRepository + +/** + * 상태 JPA 엔티티를 위한 저장소 인터페이스입니다. + * Spring Data JPA를 통해 기본 CRUD 작업과 커스텀 쿼리를 제공합니다. + */ +interface StatusRepository : CrudRepository { + + /** + * 접수번호로 상태 엔티티를 조회합니다. + * + * @param receiptCode 조회할 접수번호 + * @return 조회된 상태 엔티티 (없으면 null) + */ + fun findByReceiptCode(receiptCode: Long): StatusJpaEntity? + + /** + * 접수번호로 상태 엔티티를 삭제합니다. + * + * @param receiptCode 삭제할 접수번호 + */ + fun deleteByReceiptCode(receiptCode: Long) +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/AnnounceResultUseCase.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/AnnounceResultUseCase.kt new file mode 100644 index 0000000..8d7a959 --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/AnnounceResultUseCase.kt @@ -0,0 +1,16 @@ +package hs.kr.entrydsm.status.domain.status.application.port.`in` + +/** + * 합격 결과 발표 유스케이스 인터페이스입니다. + * 최종 전형 결과를 발표하고 상태를 변경하는 기능을 정의합니다. + */ +interface AnnounceResultUseCase { + + /** + * 합격 결과를 발표합니다. + * 전형 진행 중 상태에서 합격 여부 확인 상태로 변경합니다. + * + * @param receiptCode 접수번호 + */ + fun execute(receiptCode: Long) +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/CancelApplicationSubmitUseCase.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/CancelApplicationSubmitUseCase.kt new file mode 100644 index 0000000..35d4784 --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/CancelApplicationSubmitUseCase.kt @@ -0,0 +1,16 @@ +package hs.kr.entrydsm.status.domain.status.application.port.`in` + +/** + * 지원서 제출 취소 유스케이스 인터페이스입니다. + * 제출된 지원서를 작성 중 상태로 되돌리는 기능을 정의합니다. + */ +interface CancelApplicationSubmitUseCase { + + /** + * 지원서 제출을 취소합니다. + * 제출 완료 상태에서 작성 중 상태로 변경합니다. + * + * @param receiptCode 접수번호 + */ + fun execute(receiptCode: Long) +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/CreateStatusUseCase.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/CreateStatusUseCase.kt new file mode 100644 index 0000000..bbacd13 --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/CreateStatusUseCase.kt @@ -0,0 +1,15 @@ +package hs.kr.entrydsm.status.domain.status.application.port.`in` + +/** + * 새로운 상태를 생성하는 유스케이스 인터페이스입니다. + * 지원자가 최초로 원서를 접수할 때 상태 정보를 생성합니다. + */ +interface CreateStatusUseCase { + + /** + * 접수번호를 기반으로 새로운 상태를 생성합니다. + * + * @param receiptCode 생성할 상태의 접수번호 + */ + fun execute(receiptCode: Long) +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/DeleteStatusUseCase.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/DeleteStatusUseCase.kt new file mode 100644 index 0000000..31c9683 --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/DeleteStatusUseCase.kt @@ -0,0 +1,15 @@ +package hs.kr.entrydsm.status.domain.status.application.port.`in` + +/** + * 상태 삭제 유스케이스 인터페이스입니다. + * 특정 접수번호의 상태 정보를 삭제하는 기능을 정의합니다. + */ +interface DeleteStatusUseCase { + + /** + * 접수번호로 상태 정보를 삭제합니다. + * + * @param receiptCode 삭제할 접수번호 + */ + fun execute(receiptCode: Long) +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/GetAllStatusUseCase.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/GetAllStatusUseCase.kt new file mode 100644 index 0000000..d2f79fb --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/GetAllStatusUseCase.kt @@ -0,0 +1,17 @@ +package hs.kr.entrydsm.status.domain.status.application.port.`in` + +import hs.kr.entrydsm.status.infrastructure.grpc.server.dto.response.InternalStatusResponse + +/** + * 모든 상태 조회 유스케이스 인터페이스입니다. + * 전체 지원자의 상태 정보를 조회하는 기능을 정의합니다. + */ +interface GetAllStatusUseCase { + + /** + * 모든 상태 정보를 조회합니다. + * + * @return 모든 상태 정보를 포함한 응답 리스트 + */ + fun execute(): List +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/GetStatusByReceiptCodeUseCase.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/GetStatusByReceiptCodeUseCase.kt new file mode 100644 index 0000000..decb8b3 --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/GetStatusByReceiptCodeUseCase.kt @@ -0,0 +1,18 @@ +package hs.kr.entrydsm.status.domain.status.application.port.`in` + +import hs.kr.entrydsm.status.infrastructure.grpc.server.dto.response.InternalStatusResponse + +/** + * 접수번호로 상태 조회 유스케이스 인터페이스입니다. + * 특정 접수번호의 상태 정보를 조회하는 기능을 정의합니다. + */ +interface GetStatusByReceiptCodeUseCase { + + /** + * 접수번호로 상태 정보를 조회합니다. + * + * @param receiptCode 조회할 접수번호 + * @return 해당 접수번호의 상태 정보 응답 + */ + fun execute(receiptCode: Long): InternalStatusResponse +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/StartScreeningUseCase.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/StartScreeningUseCase.kt new file mode 100644 index 0000000..99e84bd --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/StartScreeningUseCase.kt @@ -0,0 +1,16 @@ +package hs.kr.entrydsm.status.domain.status.application.port.`in` + +/** + * 전형 시작 유스케이스 인터페이스입니다. + * 서류 접수 완료 후 1차 또는 2차 전형을 시작하는 기능을 정의합니다. + */ +interface StartScreeningUseCase { + + /** + * 전형을 시작합니다. + * 서류 접수 완료 상태에서 전형 진행 중 상태로 변경합니다. + * + * @param receiptCode 접수번호 + */ + fun execute(receiptCode: Long) +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/UpdateExamCodeUseCase.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/UpdateExamCodeUseCase.kt new file mode 100644 index 0000000..385952a --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/UpdateExamCodeUseCase.kt @@ -0,0 +1,16 @@ +package hs.kr.entrydsm.status.domain.status.application.port.`in` + +/** + * 수험번호 업데이트 유스케이스 인터페이스입니다. + * 지원자의 수험번호를 변경하는 기능을 정의합니다. + */ +interface UpdateExamCodeUseCase { + + /** + * 수험번호를 업데이트합니다. + * + * @param receiptCode 접수번호 + * @param examCode 새로운 수험번호 + */ + fun execute(receiptCode: Long, examCode: String) +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/UpdateIsPrintsArrivedUseCase.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/UpdateIsPrintsArrivedUseCase.kt new file mode 100644 index 0000000..35852a4 --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/UpdateIsPrintsArrivedUseCase.kt @@ -0,0 +1,16 @@ +package hs.kr.entrydsm.status.domain.status.application.port.`in` + +/** + * 서류 도착 확인 유스케이스 인터페이스입니다. + * 등기우편으로 제출된 서류의 도착을 확인하고 상태를 변경하는 기능을 정의합니다. + */ +interface UpdateIsPrintsArrivedUseCase { + + /** + * 서류 도착을 확인합니다. + * 서류 도착 대기 상태에서 서류 접수 완료 상태로 변경합니다. + * + * @param receiptCode 접수번호 + */ + fun execute(receiptCode: Long) +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/UpdateStatusUseCase.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/UpdateStatusUseCase.kt new file mode 100644 index 0000000..f0e2aed --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/in/UpdateStatusUseCase.kt @@ -0,0 +1,15 @@ +package hs.kr.entrydsm.status.domain.status.application.port.`in` + +/** + * 상태 업데이트 유스케이스 인터페이스입니다. + * 지원자의 상태 정보를 업데이트하는 기능을 정의합니다. + */ +interface UpdateStatusUseCase { + + /** + * 상태를 업데이트합니다. + * + * @param receiptCode 접수번호 + */ + fun execute(receiptCode: Long) +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/out/DeleteStatusPort.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/out/DeleteStatusPort.kt new file mode 100644 index 0000000..5b214ff --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/out/DeleteStatusPort.kt @@ -0,0 +1,15 @@ +package hs.kr.entrydsm.status.domain.status.application.port.out + +/** + * 상태 삭제 작업을 위한 포트 인터페이스입니다. + * 헥사고날 아키텍처에서 도메인 계층이 인프라스트럭처 계층과 통신하기 위한 인터페이스입니다. + */ +interface DeleteStatusPort { + + /** + * 접수번호로 상태 정보를 삭제합니다. + * + * @param receiptCode 삭제할 접수번호 + */ + fun deleteByReceiptCode(receiptCode: Long) +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/out/QueryStatusPort.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/out/QueryStatusPort.kt new file mode 100644 index 0000000..c542820 --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/out/QueryStatusPort.kt @@ -0,0 +1,26 @@ +package hs.kr.entrydsm.status.domain.status.application.port.out + +import hs.kr.entrydsm.status.domain.status.model.Status + +/** + * 상태 조회 작업을 위한 포트 인터페이스입니다. + * 헥사고날 아키텍처에서 도메인 계층이 인프라스트럭처 계층과 통신하기 위한 인터페이스입니다. + */ +interface QueryStatusPort { + + /** + * 접수번호로 상태 정보를 조회합니다. + * + * @param receiptCode 조회할 접수번호 + * @return 조회된 상태 도메인 모델 (없으면 null) + */ + fun findByReceiptCode(receiptCode: Long): Status? + + /** + * 모든 상태 정보를 조회합니다. + * + * @return 모든 상태 도메인 모델 리스트 + */ + fun findAll(): List + +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/out/SaveStatusPort.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/out/SaveStatusPort.kt new file mode 100644 index 0000000..4250041 --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/out/SaveStatusPort.kt @@ -0,0 +1,16 @@ +package hs.kr.entrydsm.status.domain.status.application.port.out + +import hs.kr.entrydsm.status.domain.status.model.Status + +/** + * 상태 저장 작업을 위한 포트 인터페이스입니다. + * 헥사고날 아키텍처에서 도메인 계층이 인프라스트럭처 계층과 통신하기 위한 인터페이스입니다. + */ +interface SaveStatusPort { + /** + * 상태 정보를 저장합니다. + * + * @param status 저장할 상태 도메인 모델 + */ + fun save(status: Status) +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/out/StatusPort.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/out/StatusPort.kt new file mode 100644 index 0000000..ca381b3 --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/port/out/StatusPort.kt @@ -0,0 +1,7 @@ +package hs.kr.entrydsm.status.domain.status.application.port.out + +/** + * 상태 관련 모든 포트 인터페이스를 통합한 포트입니다. + * 상태 데이터의 CRUD 작업을 위한 모든 인터페이스를 상속받습니다. + */ +interface StatusPort: DeleteStatusPort, QueryStatusPort, SaveStatusPort diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/AnnounceResultService.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/AnnounceResultService.kt new file mode 100644 index 0000000..3961bf1 --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/AnnounceResultService.kt @@ -0,0 +1,38 @@ +package hs.kr.entrydsm.status.domain.status.application.service + +import hs.kr.entrydsm.status.domain.status.application.port.`in`.AnnounceResultUseCase +import hs.kr.entrydsm.status.domain.status.application.port.out.QueryStatusPort +import hs.kr.entrydsm.status.domain.status.application.port.out.SaveStatusPort +import hs.kr.entrydsm.status.domain.status.exception.StatusNotFoundException +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +/** + * 합격 결과 발표 서비스 클래스입니다. + * 전형 진행 중인 지원자의 상태를 합격 여부 확인 상태로 변경합니다. + * + * @property queryStatusPort 상태 조회 포트 + * @property saveStatusPort 상태 저장 포트 + */ +@Transactional +@Service +class AnnounceResultService( + private val queryStatusPort: QueryStatusPort, + private val saveStatusPort: SaveStatusPort +) : AnnounceResultUseCase { + + /** + * 합격 결과를 발표합니다. + * 전형 진행 중 상태에서 합격 여부 확인 상태로 변경합니다. + * + * @param receiptCode 접수번호 + * @throws StatusNotFoundException 해당 접수번호의 상태가 존재하지 않는 경우 + */ + override fun execute(receiptCode: Long) { + val status = queryStatusPort.findByReceiptCode(receiptCode) + ?: throw StatusNotFoundException + + val resultStatus = status.announceResult() + saveStatusPort.save(resultStatus) + } +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/CancelApplicationSubmitService.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/CancelApplicationSubmitService.kt new file mode 100644 index 0000000..c409ce5 --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/CancelApplicationSubmitService.kt @@ -0,0 +1,38 @@ +package hs.kr.entrydsm.status.domain.status.application.service + +import hs.kr.entrydsm.status.domain.status.application.port.`in`.CancelApplicationSubmitUseCase +import hs.kr.entrydsm.status.domain.status.application.port.out.QueryStatusPort +import hs.kr.entrydsm.status.domain.status.application.port.out.SaveStatusPort +import hs.kr.entrydsm.status.domain.status.exception.StatusNotFoundException +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +/** + * 지원서 제출 취소 서비스 클래스입니다. + * 제출된 지원서를 작성 중 상태로 되돌립니다. + * + * @property queryStatusPort 상태 조회 포트 + * @property saveStatusPort 상태 저장 포트 + */ +@Service +class CancelApplicationSubmitService( + private val queryStatusPort: QueryStatusPort, + private val saveStatusPort: SaveStatusPort +): CancelApplicationSubmitUseCase { + + /** + * 지원서 제출을 취소합니다. + * 제출된 지원서를 작성 중 상태로 되돌립니다. + * + * @param receiptCode 접수번호 + * @throws StatusNotFoundException 해당 접수번호의 상태가 존재하지 않는 경우 + */ + @Transactional + override fun execute(receiptCode: Long) { + val status = queryStatusPort.findByReceiptCode(receiptCode) + ?: throw StatusNotFoundException + + val canceledStatus = status.cancelSubmit() + saveStatusPort.save(canceledStatus) + } +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/CreateStatusService.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/CreateStatusService.kt new file mode 100644 index 0000000..1c1c211 --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/CreateStatusService.kt @@ -0,0 +1,43 @@ +package hs.kr.entrydsm.status.domain.status.application.service + +import hs.kr.entrydsm.status.domain.status.application.port.`in`.CreateStatusUseCase +import hs.kr.entrydsm.status.domain.status.application.port.out.QueryStatusPort +import hs.kr.entrydsm.status.domain.status.application.port.out.SaveStatusPort +import hs.kr.entrydsm.status.domain.status.model.ApplicationStatus +import hs.kr.entrydsm.status.domain.status.model.Status +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +/** + * 상태 생성 서비스 클래스입니다. + * 새로운 지원자의 초기 상태를 생성합니다. + * + * @property queryStatusPort 상태 조회 포트 + * @property saveStatusPort 상태 저장 포트 + */ +@Service +class CreateStatusService( + private val queryStatusPort: QueryStatusPort, + private val saveStatusPort: SaveStatusPort +): CreateStatusUseCase { + + /** + * 새로운 지원자의 초기 상태를 생성합니다. + * 이미 상태가 존재하는 경우 생성하지 않습니다. + * + * @param receiptCode 접수번호 + */ + @Transactional + override fun execute(receiptCode: Long) { + queryStatusPort.findByReceiptCode(receiptCode)?: saveStatusPort.save( + Status( + id = null, + applicationStatus = ApplicationStatus.NOT_APPLIED, + examCode = null, + isFirstRoundPass = false, + isSecondRoundPass = false, + receiptCode = receiptCode, + ) + ) + } +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/DeleteStatusService.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/DeleteStatusService.kt new file mode 100644 index 0000000..f9a1c8f --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/DeleteStatusService.kt @@ -0,0 +1,29 @@ +package hs.kr.entrydsm.status.domain.status.application.service + +import hs.kr.entrydsm.status.domain.status.adapter.out.persistence.repository.StatusRepository +import hs.kr.entrydsm.status.domain.status.application.port.`in`.DeleteStatusUseCase +import hs.kr.entrydsm.status.domain.status.application.port.out.DeleteStatusPort +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +/** + * 상태 삭제 서비스 클래스입니다. + * 특정 접수번호의 상태 정보를 삭제합니다. + * + * @property deleteStatusPort 상태 삭제 포트 + */ +@Service +class DeleteStatusService( + private val deleteStatusPort: DeleteStatusPort +): DeleteStatusUseCase { + + /** + * 접수번호로 상태 정보를 삭제합니다. + * + * @param receiptCode 삭제할 접수번호 + */ + @Transactional + override fun execute(receiptCode: Long) { + deleteStatusPort.deleteByReceiptCode(receiptCode) + } +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/GetAllStatusService.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/GetAllStatusService.kt new file mode 100644 index 0000000..5376386 --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/GetAllStatusService.kt @@ -0,0 +1,40 @@ +package hs.kr.entrydsm.status.domain.status.application.service + +import hs.kr.entrydsm.status.infrastructure.grpc.server.dto.response.InternalStatusResponse +import hs.kr.entrydsm.status.domain.status.application.port.`in`.GetAllStatusUseCase +import hs.kr.entrydsm.status.domain.status.application.port.out.QueryStatusPort +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +/** + * 모든 상태 조회 서비스 클래스입니다. + * 전체 지원자의 상태 정보를 조회하고 응답 형태로 변환합니다. + * + * @property queryStatusPort 상태 조회 포트 + */ +@Transactional(readOnly = true) +@Service +class GetAllStatusService( + private val queryStatusPort: QueryStatusPort, +): GetAllStatusUseCase { + + /** + * 모든 상태 정보를 조회합니다. + * + * @return 모든 상태 정보를 포함한 응답 리스트 + */ + override fun execute(): List { + val status = queryStatusPort.findAll() + + return status.map { + InternalStatusResponse( + id = it.id!!, + applicationStatus = it.applicationStatus, + receiptCode = it.receiptCode, + examCode = it.examCode, + isFirstRoundPass = it.isFirstRoundPass, + isSecondRoundPass = it.isSecondRoundPass + ) + } + } +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/GetStatusByReceiptCodeService.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/GetStatusByReceiptCodeService.kt new file mode 100644 index 0000000..e7df5f4 --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/GetStatusByReceiptCodeService.kt @@ -0,0 +1,52 @@ +package hs.kr.entrydsm.status.domain.status.application.service + +import hs.kr.entrydsm.status.infrastructure.grpc.server.dto.response.InternalStatusResponse +import hs.kr.entrydsm.status.domain.status.adapter.out.domain.StatusCache +import hs.kr.entrydsm.status.domain.status.adapter.out.persistence.repository.StatusCacheRepository +import hs.kr.entrydsm.status.domain.status.application.port.`in`.GetStatusByReceiptCodeUseCase +import hs.kr.entrydsm.status.domain.status.application.port.out.QueryStatusPort +import hs.kr.entrydsm.status.domain.status.exception.StatusNotFoundException +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +/** + * 접수번호로 상태 조회 서비스 클래스입니다. + * 특정 접수번호의 상태 정보를 조회하고 캐시를 활용하여 성능을 최적화합니다. + * + * @property queryStatusPort 상태 조회 포트 + * @property statusCacheRepository 상태 캐시 저장소 + */ +@Transactional(readOnly = true) +@Service +class GetStatusByReceiptCodeService( + private val queryStatusPort: QueryStatusPort, + private val statusCacheRepository: StatusCacheRepository +): GetStatusByReceiptCodeUseCase { + + /** + * 접수번호로 상태 정보를 조회합니다. + * 캐시가 없는 경우 새로 생성하여 저장합니다. + * + * @param receiptCode 조회할 접수번호 + * @return 해당 접수번호의 상태 정보 응답 + * @throws StatusNotFoundException 해당 접수번호의 상태가 존재하지 않는 경우 + */ + override fun execute(receiptCode: Long): InternalStatusResponse { + val status = queryStatusPort.findByReceiptCode(receiptCode) ?: throw StatusNotFoundException + + statusCacheRepository.findById(receiptCode).orElseGet { + statusCacheRepository.save(StatusCache.from(status)) + } + + return status.run { + InternalStatusResponse( + id = id!!, + receiptCode = receiptCode, + applicationStatus = applicationStatus, + examCode = examCode, + isFirstRoundPass = isFirstRoundPass, + isSecondRoundPass = isSecondRoundPass, + ) + } + } +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/StartScreeningService.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/StartScreeningService.kt new file mode 100644 index 0000000..8f157a8 --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/StartScreeningService.kt @@ -0,0 +1,34 @@ +package hs.kr.entrydsm.status.domain.status.application.service + +import hs.kr.entrydsm.status.domain.status.application.port.`in`.StartScreeningUseCase +import hs.kr.entrydsm.status.domain.status.application.port.out.QueryStatusPort +import hs.kr.entrydsm.status.domain.status.application.port.out.SaveStatusPort +import hs.kr.entrydsm.status.domain.status.exception.StatusNotFoundException +import org.springframework.stereotype.Service + +/** + * 전형 시작 서비스 클래스입니다. + * 서류 접수가 완료된 지원자의 상태를 전형 진행 중으로 변경합니다. + * + * @property queryStatusPort 상태 조회 포트 + * @property saveStatusPort 상태 저장 포트 + */ +@Service +class StartScreeningService( + private val queryStatusPort: QueryStatusPort, + private val saveStatusPort: SaveStatusPort +): StartScreeningUseCase { + + /** + * 전형을 시작합니다. + * 서류 접수 완료 상태에서 전형 진행 중 상태로 변경합니다. + * + * @param receiptCode 접수번호 + * @throws StatusNotFoundException 해당 접수번호의 상태가 존재하지 않는 경우 + */ + override fun execute(receiptCode: Long){ + val status = queryStatusPort.findByReceiptCode(receiptCode) ?: throw StatusNotFoundException + val screeningStatus = status.startScreening() + saveStatusPort.save(screeningStatus) + } +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/UpdateExamCodeService.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/UpdateExamCodeService.kt new file mode 100644 index 0000000..a23ca8b --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/UpdateExamCodeService.kt @@ -0,0 +1,37 @@ +package hs.kr.entrydsm.status.domain.status.application.service + +import hs.kr.entrydsm.status.domain.status.application.port.`in`.UpdateExamCodeUseCase +import hs.kr.entrydsm.status.domain.status.application.port.out.QueryStatusPort +import hs.kr.entrydsm.status.domain.status.application.port.out.SaveStatusPort +import hs.kr.entrydsm.status.domain.status.exception.StatusNotFoundException +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +/** + * 수험번호 업데이트 서비스 클래스입니다. + * 지원자의 수험번호를 변경하는 기능을 제공합니다. + * + * @property queryStatusPort 상태 조회 포트 + * @property saveStatusPort 상태 저장 포트 + */ +@Service +class UpdateExamCodeService( + private val queryStatusPort: QueryStatusPort, + private val saveStatusPort: SaveStatusPort +): UpdateExamCodeUseCase { + + /** + * 수험번호를 업데이트합니다. + * + * @param receiptCode 접수번호 + * @param examCode 새로운 수험번호 + * @throws StatusNotFoundException 해당 접수번호의 상태가 존재하지 않는 경우 + */ + @Transactional + override fun execute(receiptCode: Long, examCode: String) { + val status = queryStatusPort.findByReceiptCode(receiptCode) ?: throw StatusNotFoundException + + val updatedExamCode = status.changeExamCode(examCode) + saveStatusPort.save(updatedExamCode) + } +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/UpdateIsPrintsArrivedService.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/UpdateIsPrintsArrivedService.kt new file mode 100644 index 0000000..5cc536d --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/UpdateIsPrintsArrivedService.kt @@ -0,0 +1,38 @@ +package hs.kr.entrydsm.status.domain.status.application.service + +import hs.kr.entrydsm.status.domain.status.application.port.`in`.UpdateIsPrintsArrivedUseCase +import hs.kr.entrydsm.status.domain.status.application.port.out.QueryStatusPort +import hs.kr.entrydsm.status.domain.status.application.port.out.SaveStatusPort +import hs.kr.entrydsm.status.domain.status.exception.StatusNotFoundException +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +/** + * 서류 도착 상태 업데이트 서비스 클래스입니다. + * 등기우편으로 제출된 서류의 도착을 확인하고 상태를 변경합니다. + * + * @property queryStatusPort 상태 조회 포트 + * @property saveStatusPort 상태 저장 포트 + */ +@Transactional +@Service +class UpdateIsPrintsArrivedService( + private val queryStatusPort: QueryStatusPort, + private val saveStatusPort: SaveStatusPort, +): UpdateIsPrintsArrivedUseCase { + + /** + * 서류 도착 상태를 업데이트합니다. + * 서류 도착 대기 상태에서 서류 접수 완료 상태로 변경합니다. + * + * @param receiptCode 접수번호 + * @throws StatusNotFoundException 해당 접수번호의 상태가 존재하지 않는 경우 + */ + override fun execute(receiptCode: Long) { + val status = queryStatusPort.findByReceiptCode(receiptCode) + ?: throw StatusNotFoundException + + val updatedStatus = status.markDocumentsArrived() + saveStatusPort.save(updatedStatus) + } +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/UpdateStatusService.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/UpdateStatusService.kt new file mode 100644 index 0000000..cd26bd3 --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/application/service/UpdateStatusService.kt @@ -0,0 +1,40 @@ +package hs.kr.entrydsm.status.domain.status.application.service + +import hs.kr.entrydsm.status.domain.status.application.port.`in`.UpdateStatusUseCase +import hs.kr.entrydsm.status.domain.status.application.port.out.QueryStatusPort +import hs.kr.entrydsm.status.domain.status.application.port.out.SaveStatusPort +import hs.kr.entrydsm.status.domain.status.exception.StatusNotFoundException +import hs.kr.entrydsm.status.domain.status.model.ApplicationStatus +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +/** + * 상태 업데이트 서비스 클래스입니다. + * 지원서 제출 시 상태를 제출 완료에서 서류 도착 대기 상태로 자동 변경합니다. + * + * @property queryStatusPort 상태 조회 포트 + * @property saveStatusPort 상태 저장 포트 + */ +@Transactional +@Service +class UpdateStatusService( + private val queryStatusPort: QueryStatusPort, + private val saveStatusPort: SaveStatusPort +): UpdateStatusUseCase { + + /** + * 지원서 제출 상태를 업데이트합니다. + * 제출 상태로 변경한 후 자동으로 서류 도착 대기 상태로 변경합니다. + * + * @param receiptCode 접수번호 + * @throws StatusNotFoundException 해당 접수번호의 상태가 존재하지 않는 경우 + */ + override fun execute(receiptCode: Long) { + val status = queryStatusPort.findByReceiptCode(receiptCode) ?: throw StatusNotFoundException + + // SUBMITTED 상태로 변경 후 바로 WAITING_DOCUMENTS로 변경 + val submittedStatus = status.submitApplication() + val waitingStatus = submittedStatus.copy(applicationStatus = ApplicationStatus.WAITING_DOCUMENTS) + saveStatusPort.save(waitingStatus) + } +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/exception/StatusNotFoundException.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/exception/StatusNotFoundException.kt new file mode 100644 index 0000000..e76df20 --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/exception/StatusNotFoundException.kt @@ -0,0 +1,12 @@ +package hs.kr.entrydsm.status.domain.status.exception + +import hs.kr.entrydsm.status.global.error.exception.CasperException +import hs.kr.entrydsm.status.global.error.exception.ErrorCode + +/** + * 상태를 찾을 수 없을 때 발생하는 예외입니다. + * 요청한 접수번호에 해당하는 상태 정보가 존재하지 않는 경우 사용됩니다. + */ +object StatusNotFoundException : CasperException(ErrorCode.STATUS_NOT_FOUND) { + val EXCEPTION = StatusNotFoundException +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/model/ApplicationStatus.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/model/ApplicationStatus.kt new file mode 100644 index 0000000..246c451 --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/model/ApplicationStatus.kt @@ -0,0 +1,28 @@ +package hs.kr.entrydsm.status.domain.status.model + +/** + * 지원서 상태를 나타내는 열거형 클래스입니다. + * 지원자의 전형 진행 단계를 7단계로 구분하여 관리합니다. + */ +enum class ApplicationStatus { + /** 미지원 - 지원자가 원서를 작성하지 않은 상태 */ + NOT_APPLIED, + + /** 원서 작성 중 - 원서가 저장되었으나 제출되지 않은 상태 */ + WRITING, + + /** 지원 완료 - 온라인 원서 접수 완료 상태 */ + SUBMITTED, + + /** 서류 도착 대기 - 원서 제출 후 학교에서 접수 확인 전 상태 */ + WAITING_DOCUMENTS, + + /** 서류 접수 완료 - 학교에서 원서 및 증빙 서류가 정상적으로 도착하여 검토 완료된 상태 */ + DOCUMENTS_RECEIVED, + + /** 전형 진행 중 - 1차 또는 2차 전형이 진행 중인 상태 */ + SCREENING_IN_PROGRESS, + + /** 합격 여부 확인 - 최종 합격자 발표 완료 상태 */ + RESULT_ANNOUNCED +} diff --git a/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/model/Status.kt b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/model/Status.kt new file mode 100644 index 0000000..026d35e --- /dev/null +++ b/casper-status/src/main/kotlin/hs/kr/entrydsm/status/domain/status/model/Status.kt @@ -0,0 +1,76 @@ +package hs.kr.entrydsm.status.domain.status.model + +/** + * 지원 상태 도메인 모델 클래스입니다. + * 지원자의 전형 진행 상태와 관련 정보를 관리합니다. + * 불변성을 보장하는 데이터 클래스로 설계되었습니다. + * + * @property id 상태 식별자 + * @property applicationStatus 현재 지원 상태 + * @property examCode 수험번호 + * @property isFirstRoundPass 1차 전형 합격 여부 + * @property isSecondRoundPass 2차 전형 합격 여부 + * @property receiptCode 접수번호 + */ +data class Status( + val id: Long?, + val applicationStatus: ApplicationStatus, + val examCode: String?, + val isFirstRoundPass: Boolean, + val isSecondRoundPass: Boolean, + val receiptCode: Long, +) { + + /** + * 지원서를 제출 상태로 변경합니다. + * + * @return 제출 상태로 변경된 Status 인스턴스 + */ + fun submitApplication(): Status { + return copy(applicationStatus = ApplicationStatus.SUBMITTED) + } + + /** + * 지원서 제출을 취소하고 작성 중 상태로 변경합니다. + * + * @return 작성 중 상태로 변경된 Status 인스턴스 + */ + fun cancelSubmit(): Status { + return copy(applicationStatus = ApplicationStatus.WRITING) + } + + /** + * 서류가 도착했음을 표시하고 서류 접수 완료 상태로 변경합니다. + * + * @return 서류 접수 완료 상태로 변경된 Status 인스턴스 + */ + fun markDocumentsArrived(): Status { + return copy(applicationStatus = ApplicationStatus.DOCUMENTS_RECEIVED) + } + + /** + * 전형을 시작하고 전형 진행 중 상태로 변경합니다. + * + * @return 전형 진행 중 상태로 변경된 Status 인스턴스 + */ + fun startScreening(): Status { + return copy(applicationStatus = ApplicationStatus.SCREENING_IN_PROGRESS) + } + + /** + * 합격 결과를 발표하고 합격 여부 확인 상태로 변경합니다. + * + * @return 합격 여부 확인 상태로 변경된 Status 인스턴스 + */ + fun announceResult(): Status { + return copy(applicationStatus = ApplicationStatus.RESULT_ANNOUNCED) + } + + /** + * 수험번호를 변경합니다. + * + * @param examCode 새로운 수험번호 + * @return 수험번호가 변경된 Status 인스턴스 + */ + fun changeExamCode(examCode: String): Status = copy(examCode = examCode) +}