diff --git a/src/main/java/com/fowoco/server/casework/application/CaseDisplayFacts.java b/src/main/java/com/fowoco/server/casework/application/CaseDisplayFacts.java new file mode 100644 index 0000000..6f983b6 --- /dev/null +++ b/src/main/java/com/fowoco/server/casework/application/CaseDisplayFacts.java @@ -0,0 +1,17 @@ +package com.fowoco.server.casework.application; + +/** + * Case 표시 상태를 계산할 때 사용하는 조회 사실입니다. + * + * @param completed Case 업무가 모두 완료됐는지 여부 + * @param linkIssued 근로자용 요청 링크를 발급했는지 여부 + * @param reviewRequired 도착한 답변이나 서류에 HR 검토가 남아 있는지 여부 + * @param unreadResponse 도착했지만 아직 읽지 않은 근로자 응답이 있는지 여부 + */ +public record CaseDisplayFacts( + boolean completed, + boolean linkIssued, + boolean reviewRequired, + boolean unreadResponse +) { +} diff --git a/src/main/java/com/fowoco/server/casework/application/CaseDisplayState.java b/src/main/java/com/fowoco/server/casework/application/CaseDisplayState.java new file mode 100644 index 0000000..58cd802 --- /dev/null +++ b/src/main/java/com/fowoco/server/casework/application/CaseDisplayState.java @@ -0,0 +1,16 @@ +package com.fowoco.server.casework.application; + +import com.fowoco.server.casework.domain.CaseDisplayStatus; +import java.util.Objects; + +/** + * 업무함 API에 반환할 Case 표시 상태와 새 응답 표시입니다. + */ +public record CaseDisplayState( + CaseDisplayStatus status, + boolean hasUnreadResponse +) { + public CaseDisplayState { + Objects.requireNonNull(status, "status must not be null"); + } +} diff --git a/src/main/java/com/fowoco/server/casework/application/CaseDisplayStatusResolver.java b/src/main/java/com/fowoco/server/casework/application/CaseDisplayStatusResolver.java new file mode 100644 index 0000000..139d418 --- /dev/null +++ b/src/main/java/com/fowoco/server/casework/application/CaseDisplayStatusResolver.java @@ -0,0 +1,28 @@ +package com.fowoco.server.casework.application; + +import com.fowoco.server.casework.domain.CaseDisplayStatus; +import java.util.Objects; + +/** + * 여러 모듈의 상태를 변경하지 않고 업무함 표시 상태만 계산합니다. + */ +public final class CaseDisplayStatusResolver { + + public CaseDisplayState resolve(CaseDisplayFacts facts) { + Objects.requireNonNull(facts, "facts must not be null"); + + if (facts.completed()) { + return new CaseDisplayState(CaseDisplayStatus.COMPLETED, false); + } + if (facts.reviewRequired() || facts.unreadResponse()) { + return new CaseDisplayState( + CaseDisplayStatus.REVIEW_REQUIRED, + facts.unreadResponse() + ); + } + if (facts.linkIssued()) { + return new CaseDisplayState(CaseDisplayStatus.REQUEST_SENT, false); + } + return new CaseDisplayState(CaseDisplayStatus.DOCUMENT_PENDING, false); + } +} diff --git a/src/main/java/com/fowoco/server/casework/domain/CaseDisplayStatus.java b/src/main/java/com/fowoco/server/casework/domain/CaseDisplayStatus.java new file mode 100644 index 0000000..e0328e2 --- /dev/null +++ b/src/main/java/com/fowoco/server/casework/domain/CaseDisplayStatus.java @@ -0,0 +1,14 @@ +package com.fowoco.server.casework.domain; + +/** + * 업무함에서 사용자에게 보여 주는 Case 요약 상태입니다. + * + *
Task의 업무 처리 상태와 Worker Link의 전달 상태를 바꾸지 않고, + * 조회 시점의 사실을 조합해 화면에 표시할 값만 나타냅니다.
+ */ +public enum CaseDisplayStatus { + DOCUMENT_PENDING, + REQUEST_SENT, + REVIEW_REQUIRED, + COMPLETED +} diff --git a/src/main/java/com/fowoco/server/casework/package-info.java b/src/main/java/com/fowoco/server/casework/package-info.java new file mode 100644 index 0000000..5a7e0f6 --- /dev/null +++ b/src/main/java/com/fowoco/server/casework/package-info.java @@ -0,0 +1,4 @@ +/** + * Case grouping, workflow snapshots, and work-inbox projections. + */ +package com.fowoco.server.casework; diff --git a/src/test/java/com/fowoco/server/casework/application/CaseDisplayStatusResolverTest.java b/src/test/java/com/fowoco/server/casework/application/CaseDisplayStatusResolverTest.java new file mode 100644 index 0000000..c7e576f --- /dev/null +++ b/src/test/java/com/fowoco/server/casework/application/CaseDisplayStatusResolverTest.java @@ -0,0 +1,68 @@ +package com.fowoco.server.casework.application; + +import static com.fowoco.server.casework.domain.CaseDisplayStatus.COMPLETED; +import static com.fowoco.server.casework.domain.CaseDisplayStatus.DOCUMENT_PENDING; +import static com.fowoco.server.casework.domain.CaseDisplayStatus.REQUEST_SENT; +import static com.fowoco.server.casework.domain.CaseDisplayStatus.REVIEW_REQUIRED; +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class CaseDisplayStatusResolverTest { + + private final CaseDisplayStatusResolver resolver = new CaseDisplayStatusResolver(); + + @Test + void showsDocumentPendingBeforeWorkerLinkIsIssued() { + CaseDisplayState result = resolver.resolve(facts(false, false, false, false)); + + assertThat(result.status()).isEqualTo(DOCUMENT_PENDING); + assertThat(result.hasUnreadResponse()).isFalse(); + } + + @Test + void showsRequestSentAfterWorkerLinkIsIssuedWithoutResponse() { + CaseDisplayState result = resolver.resolve(facts(false, true, false, false)); + + assertThat(result.status()).isEqualTo(REQUEST_SENT); + assertThat(result.hasUnreadResponse()).isFalse(); + } + + @Test + void keepsReviewRequiredAfterResponseIsReadUntilHrDecision() { + CaseDisplayState result = resolver.resolve(facts(false, true, true, false)); + + assertThat(result.status()).isEqualTo(REVIEW_REQUIRED); + assertThat(result.hasUnreadResponse()).isFalse(); + } + + @Test + void showsReviewRequiredAndUnreadBadgeForNewWorkerResponse() { + CaseDisplayState result = resolver.resolve(facts(false, true, false, true)); + + assertThat(result.status()).isEqualTo(REVIEW_REQUIRED); + assertThat(result.hasUnreadResponse()).isTrue(); + } + + @Test + void completedTakesPriorityAndClearsUnreadBadge() { + CaseDisplayState result = resolver.resolve(facts(true, true, true, true)); + + assertThat(result.status()).isEqualTo(COMPLETED); + assertThat(result.hasUnreadResponse()).isFalse(); + } + + private CaseDisplayFacts facts( + boolean completed, + boolean linkIssued, + boolean reviewRequired, + boolean unreadResponse + ) { + return new CaseDisplayFacts( + completed, + linkIssued, + reviewRequired, + unreadResponse + ); + } +}