Skip to content

[FEAT] E 인수인계 인사이트/패키지 read model + 회의 조회 어댑터(#66 연동) - #71

Merged
mosungjin merged 6 commits into
developfrom
feat/mo-handover-insight
Aug 5, 2026
Merged

[FEAT] E 인수인계 인사이트/패키지 read model + 회의 조회 어댑터(#66 연동)#71
mosungjin merged 6 commits into
developfrom
feat/mo-handover-insight

Conversation

@jongjunn

@jongjunn jongjunn commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

연관

작업 내용

  • E 인계 패키지 조회 read model (GetHandoverPackage)
  • E 인수인계 인사이트 "레거시 컴파일러" — finalize 파생 스냅샷 (HandoverInsightFinalizeService, HandoverInsight, V7.4 마이그레이션)
  • E→D 회의 조회 어댑터 (MeetingQueryPortDelegatingAdapter): E 아웃바운드 handover.port.out.MeetingQueryPort → D 인바운드 meeting.port.in.MeetingQueryPort 위임

어댑터 계약 처리 (리뷰 포인트)

  • companyId: 요청 시점 AuthPrincipal.getCompanyId()에서 조달. principal/companyId 부재 시 fail-fast 예외(조용한 fallback 아님).
  • 회의 없음: D Optional empty → HO_NOT_FOUND fail-fast.
  • decisionSummary·actionItemsSummary: A·C 도메인 소유라 [FEAT] 회의 참석자 조회 및 인수인계 조회 Port 추가 #66 결과에 없음 → 의도된 null(주석 명시, 추후 A/C 연동).
  • topicId·parentTopicId: D MeetingTopicResult 미제공 → null. topicType = type.name().
  • 참석자 이름: D 내부 B(member) 연동(PendingMemberQueryAdapter)이 미구현이면 예외 전파(삼키지 않음).

검증

  • ./gradlew compileJava compileTestJava 통과, handover/adapter 단위테스트 전부 통과.
  • ⚠️ Meeting*PersistenceAdapterTest 등 8개는 로컬 H2 환경의 기존 실패로, origin/develop(baseline)에서도 동일하게 실패함 — 본 변경과 무관(CI MySQL에서 확인 필요).
  • develop과 머지 충돌 없음(CLEAN).

구현: Codex / 계획·검증·컴파일 픽스: Claude

Summary by CodeRabbit

  • 새 기능

    • 인수인계 패키지에서 기본 정보, 완료·마감 현황, 작업 항목, 컨텍스트 카드, 회의 이력, 재배정 그룹을 확인할 수 있습니다.
    • 퇴사 인수인계 완료 시 소유권, 미처리 작업, 담당자 추천, 관련 회의 흐름 등의 인사이트가 자동 생성됩니다.
    • 회의 주제와 상위 주제 정보를 함께 확인할 수 있습니다.
  • 개선 사항

    • 인사이트가 인수인계별로 최신 상태로 갱신됩니다.
    • 회의 및 인수인계 데이터 조회와 오류 처리가 강화되었습니다.

jongjunn and others added 4 commits August 5, 2026 16:25
후임자용 인계서 조회. Handover 스냅샷 + 회의 이력(MeetingQueryPort)을
기본정보/공백요약/항목/컨텍스트카드/회의이력/재배정그룹으로 조립.
- application/usecase/GetHandoverPackageUseCase, service/HandoverPackageService
- port/out/MeetingQueryPort (findMeeting 베이스만; 인사이트 메서드는 후속 PR)
- 완료 판정은 도메인 HandoverActionStatus 재사용(로직 중복 제거)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
OFFBOARDING 최종승인 트랜잭션 내에서 오너십·고아경보·질문라우팅·회의맥락
타임라인 4종 인사이트를 조립·JSON 스냅샷 저장.
- domain/model/HandoverInsight(Kind), application/service/HandoverInsightFinalizeService
- port/out/HandoverInsightPort + Action/Org/Meeting 포트에 인사이트 메서드·record 증분
- infrastructure/persistence/HandoverInsight*(엔티티·리포·어댑터), V7.4 마이그레이션
- build.gradle jackson-datatype-jsr310(스냅샷 JSON 직렬화)
- 배선: HandoverService.finalize(OFFBOARDING) + HandoverServiceTest 갱신(projectId·인사이트 목)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ActionReassignPort#findHandoverableActions(memberId)·findTeamActionsForDeparture,
OrgQueryPort#findMembers 인사이트 증분 계약을 pending 어댑터에 fail-fast 스텁으로 추가.
리베이스 시 포트 인터페이스만 확장되고 pending 어댑터가 미갱신되어 컴파일 실패하던 것 수정.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cda8fdbb-1d91-4793-96bc-3e5d20372e30

📥 Commits

Reviewing files that changed from the base of the PR and between 52e2fc8 and 8c448b8.

📒 Files selected for processing (1)
  • src/main/java/com/module06/backend/handover/infrastructure/persistence/HandoverInsightJpaRepository.java

📝 Walkthrough

Walkthrough

인수인계 패키지 조회 기능과 네 가지 인사이트 생성 기능을 추가했다. OFFBOARDING 완료 승인 시 인사이트를 생성한다. 인사이트를 handover_insight 테이블에 교체 저장한다. 회의 토픽 조회 결과에 계층 식별자를 포함했다.

Changes

인수인계 기능 확장

Layer / File(s) Summary
회의 조회 계약과 데이터 매핑
src/main/java/com/module06/backend/handover/application/port/out/MeetingQueryPort.java, src/main/java/com/module06/backend/handover/infrastructure/adapter/MeetingQueryPortDelegatingAdapter.java, src/main/java/com/module06/backend/meeting/..., src/test/java/com/module06/backend/...
회의 이력, 프로젝트 회의, 참석자, 토픽 조회 계약을 추가했다. 인증된 companyId를 하위 포트에 전달한다. 토픽 ID와 부모 토픽 ID를 결과에 포함한다.
인수인계 패키지 조회
src/main/java/com/module06/backend/handover/application/usecase/GetHandoverPackageUseCase.java, src/main/java/com/module06/backend/handover/application/service/HandoverPackageService.java, src/test/java/com/module06/backend/handover/application/service/HandoverPackageServiceTest.java
인수인계 항목을 기본 정보, 갭 요약, 컨텍스트 카드, 회의 이력, 재배정 그룹으로 변환한다. 완료 수, 미완료 수, 임박한 마감 수를 계산한다.
인사이트 생성과 완료 승인 연결
src/main/java/com/module06/backend/handover/application/..., src/main/java/com/module06/backend/handover/domain/..., src/main/java/com/module06/backend/handover/infrastructure/adapter/..., src/test/java/com/module06/backend/handover/application/service/..., build.gradle
액션, 팀 액션, 회의, 구성원 정보를 조회해 OWNERSHIP, ORPHAN_ALERT, ASK_WHOM, CONTEXT_TIMELINE 인사이트를 생성한다. OFFBOARDING 완료 승인에서 인사이트 최종화 유스케이스를 호출한다.
인사이트 저장소 구현
src/main/java/com/module06/backend/handover/infrastructure/persistence/*, src/main/resources/db/migration/V7.4__create_handover_insight.sql
handover_insight 테이블과 JPA 매핑을 추가했다. handover별 기존 인사이트를 삭제하고 새 인사이트를 일괄 저장한다.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant HandoverService
  participant HandoverInsightFinalizeService
  participant ActionReassignPort
  participant MeetingQueryPort
  participant OrgQueryPort
  participant HandoverInsightPersistenceAdapter
  HandoverService->>HandoverInsightFinalizeService: OFFBOARDING 완료 후 finalizeInsights(command)
  HandoverInsightFinalizeService->>ActionReassignPort: 액션 및 팀 액션 조회
  HandoverInsightFinalizeService->>MeetingQueryPort: 프로젝트 회의·참석자·토픽 조회
  HandoverInsightFinalizeService->>OrgQueryPort: 구성원 조회
  HandoverInsightFinalizeService->>HandoverInsightPersistenceAdapter: 인사이트 목록 교체
Loading

Possibly related PRs

Suggested reviewers: mosungjin, mnppi223

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.83% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 인수인계 인사이트·패키지 read model과 회의 조회 어댑터 추가라는 주요 변경 사항을 명확하게 요약합니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/mo-handover-insight

Comment @coderabbitai help to get the list of available commands.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
putComment timed out

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (8)
src/main/java/com/module06/backend/handover/application/service/HandoverInsightFinalizeService.java (3)

301-306: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

직렬화 실패의 원인 예외가 소실된다.

JsonProcessingExceptionHO_CONFLICT로 변환할 때 원인을 전달하지 않는다. 어떤 payload의 어떤 필드가 실패했는지 로그에서 확인할 수 없다. 원인을 BusinessException에 전달하거나 변환 전에 kindactionId를 로그로 남겨야 한다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/module06/backend/handover/application/service/HandoverInsightFinalizeService.java`
around lines 301 - 306, Update the JsonProcessingException handling in the
HandoverInsight snapshot creation flow to preserve the original exception when
converting it to BusinessException with HO_CONFLICT. Ensure the
BusinessException retains the serialization cause so payload field details
remain available in logs, using the existing constructor or cause mechanism.

81-96: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy lift

CONTEXT_TIMELINE payload가 액션마다 프로젝트 전체 타임라인을 중복 저장한다.

contextTimelinePayload는 액션의 프로젝트에 속한 모든 회의와 각 회의의 모든 토픽 본문을 직렬화한다. 같은 프로젝트에 속한 액션이 N개면 동일한 타임라인 JSON이 N번 직렬화되어 N행으로 저장된다. 저장량은 (액션 수 × 회의 수 × 토픽 수)에 비례한다.

프로젝트 단위로 타임라인 스냅샷을 1건 저장하고, 액션별 레코드에는 projectIdsourceMeetingId만 두는 구조를 검토해야 한다.

Also applies to: 276-292

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/module06/backend/handover/application/service/HandoverInsightFinalizeService.java`
around lines 81 - 96, Update HandoverInsightFinalizeService so CONTEXT_TIMELINE
is serialized and persisted once per project rather than once per action. Store
the project-level timeline snapshot in a dedicated record, and make each
action-level insight reference that snapshot using projectId and sourceMeetingId
instead of embedding the full payload. Adjust the loop around
contextTimelinePayload and the related persistence model while preserving the
existing ASK_WHOM generation.

3-5: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Jackson 3 API로 전환하세요.

Spring Boot 4.1은 Jackson 3를 기본 사용하지만, 이 서비스와 테스트는 com.fasterxml.jacksonJavaTimeModule을 사용하고 jackson-datatype-jsr310을 추가합니다. tools.jackson.databind.ObjectMapper와 내장 java.time 지원을 사용하고 해당 의존성을 제거하세요. L301-L306의 예외 처리도 tools.jackson.core.JacksonException에 맞게 변경하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/module06/backend/handover/application/service/HandoverInsightFinalizeService.java`
around lines 3 - 5, HandoverInsightFinalizeService와 관련 테스트를 Jackson 3 API로
전환하세요. com.fasterxml.jackson imports와 JavaTimeModule 사용을 제거하고
tools.jackson.databind.ObjectMapper 및 내장 java.time 지원을 사용하도록 변경하며,
jackson-datatype-jsr310 의존성도 삭제하세요. L301-L306의 예외 처리는
tools.jackson.core.JacksonException을 처리하도록 업데이트하세요.

Source: Learnings

src/main/java/com/module06/backend/handover/infrastructure/persistence/HandoverInsightPersistenceAdapter.java (1)

17-23: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

replace-all이 지연 파생 삭제와 saveAll로 구현되어 순서가 보장되지 않는다. 공통 루트 원인은 하나다. 파생 삭제가 대상 행을 로드하고 remove를 flush 시점까지 지연하며, Hibernate는 flush에서 삽입을 삭제보다 먼저 실행한다. 유니크 제약이 있으면 재확정 시 제약 위반이 발생하고, 없더라도 payload 전체가 불필요하게 메모리로 로드된다.

  • src/main/java/com/module06/backend/handover/infrastructure/persistence/HandoverInsightPersistenceAdapter.java#L17-L23: 벌크 삭제를 호출하고, 삽입 전에 삭제가 DB에 반영되도록 flush 경계를 확보한다.
  • src/main/java/com/module06/backend/handover/infrastructure/persistence/HandoverInsightJpaRepository.java#L5-L8: deleteByHandoverId@Modifying(flushAutomatically = true, clearAutomatically = true) 벌크 삭제 질의로 교체한다.
♻️ 벌크 삭제로 교체

HandoverInsightJpaRepository.java:

+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
 public interface HandoverInsightJpaRepository extends JpaRepository<HandoverInsightJpaEntity, Long> {
 
-    void deleteByHandoverId(Long handoverId);
+    `@Modifying`(flushAutomatically = true, clearAutomatically = true)
+    `@Query`("delete from HandoverInsightJpaEntity e where e.handoverId = :handoverId")
+    void deleteByHandoverId(`@Param`("handoverId") Long handoverId);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/module06/backend/handover/infrastructure/persistence/HandoverInsightPersistenceAdapter.java`
around lines 17 - 23, replaceAllForHandover in
src/main/java/com/module06/backend/handover/infrastructure/persistence/HandoverInsightPersistenceAdapter.java:17-23
must use a bulk delete and establish a flush boundary before inserting
replacement entities; update deleteByHandoverId in
src/main/java/com/module06/backend/handover/infrastructure/persistence/HandoverInsightJpaRepository.java:5-8
to be a modifying bulk-delete query with automatic flush and persistence-context
clearing, preserving the existing replacement flow.
src/main/java/com/module06/backend/handover/infrastructure/adapter/MeetingQueryPortDelegatingAdapter.java (1)

90-98: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

컨텍스트 부재를 IllegalStateException으로 던지면 500 응답이 된다.

이 모듈의 다른 실패 경로는 BusinessExceptionHandoverErrorCode를 사용한다. currentCompanyId만 표준 예외를 던진다. 인증 컨텍스트 없이 이 어댑터가 호출되면 도메인 오류 코드 없이 서버 오류로 노출된다.

HandoverErrorCode의 적절한 코드로 BusinessException을 던지도록 맞추면 응답 계약이 일관된다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/module06/backend/handover/infrastructure/adapter/MeetingQueryPortDelegatingAdapter.java`
around lines 90 - 98, Update currentCompanyId in
MeetingQueryPortDelegatingAdapter to throw BusinessException with the
appropriate HandoverErrorCode when authentication or AuthPrincipal companyId is
absent, replacing the IllegalStateException. Preserve the existing validation
and successful companyId return behavior.
src/main/java/com/module06/backend/handover/application/service/HandoverPackageService.java (2)

110-124: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

회의 이력 조회가 N+1이다.

toMeetingHistories는 회의 ID마다 findMeeting을 개별 호출한다. 위임 어댑터는 호출마다 SecurityContext 조회와 D 모듈 질의를 수행한다. 회의 수가 늘어나면 조회 횟수가 선형으로 증가한다.

MeetingQueryPort에는 이미 배치 계약(findMeetingAttendees, findMeetingTopics)이 있다. 회의 이력도 배치 조회 메서드를 추가하는 편이 일관성과 성능 모두에 유리하다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/module06/backend/handover/application/service/HandoverPackageService.java`
around lines 110 - 124, Update toMeetingHistories to avoid per-ID findMeeting
calls by adding and using a batch meeting-history query method on
MeetingQueryPort, following the existing findMeetingAttendees and
findMeetingTopics batch contracts. Preserve null filtering, ID deduplication,
and the current MeetingHistory mapping while ensuring the delegation performs
one batch query for all meeting IDs.

33-42: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

포트 부재 처리 방식이 서비스 간에 다르다.

이 서비스는 Optional<MeetingQueryPort> 주입 후 HO_CONFLICT를 던진다. HandoverInsightFinalizeServiceObjectProvider를 사용한다. 두 서비스가 같은 "미구현 포트" 상황을 다른 방식으로 표현한다. 한 방식으로 통일하면 생성자 이중화도 제거할 수 있다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/module06/backend/handover/application/service/HandoverPackageService.java`
around lines 33 - 42, HandoverPackageService의 Optional<MeetingQueryPort> 주입과 생성자
이중화를 제거하고, HandoverInsightFinalizeService와 동일하게 ObjectProvider<MeetingQueryPort>
방식으로 통일하세요. 생성자에서 ObjectProvider를 받아 미구현 포트 상황을 기존 서비스와 같은 방식으로 처리하도록
HandoverPackageService의 의존성 초기화와 HO_CONFLICT 처리 흐름을 정리하세요.
build.gradle (1)

46-48: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Jackson 3 마이그레이션 범위를 관련 코드에 일관되게 적용하십시오.

build.gradle의 Jackson 2 의존성만 제거하면 HandoverInsightFinalizeService.java가 여전히 com.fasterxml.jackson.*JavaTimeModule을 사용합니다. 서비스와 HandoverInsightFinalizeServiceTest.java의 Jackson API를 tools.jackson.*로 변경하고, Jackson 3의 기본 Java Time 지원을 사용하십시오. 테스트의 JavaTimeModule 등록도 제거하십시오.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@build.gradle` around lines 46 - 48, Apply the Jackson 3 migration
consistently: remove the Jackson 2 jsr310 dependency from build.gradle, update
HandoverInsightFinalizeService.java and HandoverInsightFinalizeServiceTest.java
imports and API usage from com.fasterxml.jackson.* to tools.jackson.*, and
remove JavaTimeModule registration from the test while relying on Jackson 3’s
built-in Java Time support.

Source: Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/com/module06/backend/handover/application/service/HandoverInsightFinalizeService.java`:
- Around line 127-128: Update the ordering logic in
HandoverInsightFinalizeService around orderedMeetings.sort and the max
calculation near ProjectMeeting.startAt to use a null-safe comparator. Apply the
same null ordering consistently to both paths, preserving normal chronological
ordering for non-null values and preventing finalize from failing when startAt
is null.

In
`@src/main/java/com/module06/backend/handover/application/service/HandoverService.java`:
- Around line 120-123: 연결된 운영 구현이 없어 OFFBOARDING 최종 승인 중 pending 어댑터가 예외를
발생시킵니다. HandoverService.finalize의 finalizeInsights 호출이 사용하는 실제
ActionReassignPort 구현 빈을 연결하고,
src/main/java/com/module06/backend/handover/infrastructure/adapter/ActionReassignPortPendingAdapter.java:22-30의
pending 구현을 대체하십시오. 또한
src/main/java/com/module06/backend/handover/infrastructure/adapter/OrgQueryPortPendingAdapter.java:26-29의
OrgQueryPort pending 구현도 실제 운영 구현 빈으로 교체하여 OrgQueryPort.findMembers가 정상 동작하게
하십시오.

In
`@src/main/java/com/module06/backend/handover/infrastructure/adapter/MeetingQueryPortDelegatingAdapter.java`:
- Around line 79-88: Update the meeting-topic query result flow from
MeetingTopicJpaEntity through MeetingTopicSnapshot and MeetingTopicResult to
MeetingQueryPort.MeetingTopic so topic id and parentTopicId are retained and
passed into toMeetingTopic. Replace the null values currently supplied by
toMeetingTopic with these propagated identifiers, preserving the existing type,
content, and sortOrder mapping.

In
`@src/test/java/com/module06/backend/handover/application/service/HandoverServiceTest.java`:
- Around line 62-70: Update the OFFBOARDING tests for
finalizeOffboardingOffboardsWriter to verify finalizeHandoverInsightsUseCase is
called exactly once with a new FinalizeHandoverInsightsCommand(HANDOVER_ID,
WRITER), and update the VACATION tests to verify there is no interaction with
this use case.

---

Nitpick comments:
In `@build.gradle`:
- Around line 46-48: Apply the Jackson 3 migration consistently: remove the
Jackson 2 jsr310 dependency from build.gradle, update
HandoverInsightFinalizeService.java and HandoverInsightFinalizeServiceTest.java
imports and API usage from com.fasterxml.jackson.* to tools.jackson.*, and
remove JavaTimeModule registration from the test while relying on Jackson 3’s
built-in Java Time support.

In
`@src/main/java/com/module06/backend/handover/application/service/HandoverInsightFinalizeService.java`:
- Around line 301-306: Update the JsonProcessingException handling in the
HandoverInsight snapshot creation flow to preserve the original exception when
converting it to BusinessException with HO_CONFLICT. Ensure the
BusinessException retains the serialization cause so payload field details
remain available in logs, using the existing constructor or cause mechanism.
- Around line 81-96: Update HandoverInsightFinalizeService so CONTEXT_TIMELINE
is serialized and persisted once per project rather than once per action. Store
the project-level timeline snapshot in a dedicated record, and make each
action-level insight reference that snapshot using projectId and sourceMeetingId
instead of embedding the full payload. Adjust the loop around
contextTimelinePayload and the related persistence model while preserving the
existing ASK_WHOM generation.
- Around line 3-5: HandoverInsightFinalizeService와 관련 테스트를 Jackson 3 API로 전환하세요.
com.fasterxml.jackson imports와 JavaTimeModule 사용을 제거하고
tools.jackson.databind.ObjectMapper 및 내장 java.time 지원을 사용하도록 변경하며,
jackson-datatype-jsr310 의존성도 삭제하세요. L301-L306의 예외 처리는
tools.jackson.core.JacksonException을 처리하도록 업데이트하세요.

In
`@src/main/java/com/module06/backend/handover/application/service/HandoverPackageService.java`:
- Around line 110-124: Update toMeetingHistories to avoid per-ID findMeeting
calls by adding and using a batch meeting-history query method on
MeetingQueryPort, following the existing findMeetingAttendees and
findMeetingTopics batch contracts. Preserve null filtering, ID deduplication,
and the current MeetingHistory mapping while ensuring the delegation performs
one batch query for all meeting IDs.
- Around line 33-42: HandoverPackageService의 Optional<MeetingQueryPort> 주입과 생성자
이중화를 제거하고, HandoverInsightFinalizeService와 동일하게 ObjectProvider<MeetingQueryPort>
방식으로 통일하세요. 생성자에서 ObjectProvider를 받아 미구현 포트 상황을 기존 서비스와 같은 방식으로 처리하도록
HandoverPackageService의 의존성 초기화와 HO_CONFLICT 처리 흐름을 정리하세요.

In
`@src/main/java/com/module06/backend/handover/infrastructure/adapter/MeetingQueryPortDelegatingAdapter.java`:
- Around line 90-98: Update currentCompanyId in
MeetingQueryPortDelegatingAdapter to throw BusinessException with the
appropriate HandoverErrorCode when authentication or AuthPrincipal companyId is
absent, replacing the IllegalStateException. Preserve the existing validation
and successful companyId return behavior.

In
`@src/main/java/com/module06/backend/handover/infrastructure/persistence/HandoverInsightPersistenceAdapter.java`:
- Around line 17-23: replaceAllForHandover in
src/main/java/com/module06/backend/handover/infrastructure/persistence/HandoverInsightPersistenceAdapter.java:17-23
must use a bulk delete and establish a flush boundary before inserting
replacement entities; update deleteByHandoverId in
src/main/java/com/module06/backend/handover/infrastructure/persistence/HandoverInsightJpaRepository.java:5-8
to be a modifying bulk-delete query with automatic flush and persistence-context
clearing, preserving the existing replacement flow.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 00a670b0-2fd4-433b-8c73-0a15d226738b

📥 Commits

Reviewing files that changed from the base of the PR and between 6f95dfc and c3a913c.

📒 Files selected for processing (24)
  • build.gradle
  • src/main/java/com/module06/backend/handover/application/command/FinalizeHandoverInsightsCommand.java
  • src/main/java/com/module06/backend/handover/application/port/out/ActionReassignPort.java
  • src/main/java/com/module06/backend/handover/application/port/out/HandoverInsightPort.java
  • src/main/java/com/module06/backend/handover/application/port/out/MeetingQueryPort.java
  • src/main/java/com/module06/backend/handover/application/port/out/OrgQueryPort.java
  • src/main/java/com/module06/backend/handover/application/service/HandoverInsightFinalizeService.java
  • src/main/java/com/module06/backend/handover/application/service/HandoverPackageService.java
  • src/main/java/com/module06/backend/handover/application/service/HandoverService.java
  • src/main/java/com/module06/backend/handover/application/usecase/FinalizeHandoverInsightsUseCase.java
  • src/main/java/com/module06/backend/handover/application/usecase/GetHandoverPackageUseCase.java
  • src/main/java/com/module06/backend/handover/domain/model/HandoverInsight.java
  • src/main/java/com/module06/backend/handover/domain/model/HandoverInsightKind.java
  • src/main/java/com/module06/backend/handover/infrastructure/adapter/ActionReassignPortPendingAdapter.java
  • src/main/java/com/module06/backend/handover/infrastructure/adapter/MeetingQueryPortDelegatingAdapter.java
  • src/main/java/com/module06/backend/handover/infrastructure/adapter/OrgQueryPortPendingAdapter.java
  • src/main/java/com/module06/backend/handover/infrastructure/persistence/HandoverInsightJpaEntity.java
  • src/main/java/com/module06/backend/handover/infrastructure/persistence/HandoverInsightJpaRepository.java
  • src/main/java/com/module06/backend/handover/infrastructure/persistence/HandoverInsightPersistenceAdapter.java
  • src/main/resources/db/migration/V7.4__create_handover_insight.sql
  • src/test/java/com/module06/backend/handover/application/service/HandoverInsightFinalizeServiceTest.java
  • src/test/java/com/module06/backend/handover/application/service/HandoverPackageServiceTest.java
  • src/test/java/com/module06/backend/handover/application/service/HandoverServiceTest.java
  • src/test/java/com/module06/backend/handover/infrastructure/adapter/MeetingQueryPortDelegatingAdapterTest.java

Comment on lines +120 to +123
// "레거시 컴파일러" 파생 인텔리전스 스냅샷을 finalize 트랜잭션 내에서 조립·저장(브리프 §4).
// 퇴사(OFFBOARDING)에만 적용. 크로스모듈 포트(C/D/B) 미구현 시 여기서 throw → 계약 대기 상태.
finalizeHandoverInsightsUseCase.finalizeInsights(
new FinalizeHandoverInsightsCommand(handoverId, handover.getWriterMemberId()));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 4 \
  'implements\s+(ActionReassignPort|OrgQueryPort)|`@Primary`|`@Profile`|`@Conditional`|`@Component`|`@Service`' \
  src/main/java

rg -n -C 4 \
  'finalizeInsights|findHandoverableActions\(command\.departureMemberId\(\)\)|findMembers\(' \
  src/main/java/com/module06/backend/handover

Repository: Z-Groupware/BACKEND

Length of output: 50375


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- relevant implementations and bean annotations ---'
rg -n -C 3 \
  'ActionReassignPort|OrgQueryPort|FinalizeHandoverInsightsUseCase|HandoverInsightFinalizeService' \
  src/main/java \
  | rg -v 'target|build' \
  | head -n 240

printf '%s\n' '--- pending adapters ---'
cat -n src/main/java/com/module06/backend/handover/infrastructure/adapter/ActionReassignPortPendingAdapter.java
cat -n src/main/java/com/module06/backend/handover/infrastructure/adapter/OrgQueryPortPendingAdapter.java

printf '%s\n' '--- insight finalization flow ---'
cat -n src/main/java/com/module06/backend/handover/application/service/HandoverInsightFinalizeService.java | sed -n '1,210p'

printf '%s\n' '--- relevant configuration and profiles ---'
rg -n -C 3 \
  'spring\.profiles|`@ActiveProfiles`|prod|ComponentScan|excludeFilters|ActionReassignPort|OrgQueryPort' \
  src/main resources .github 2>/dev/null | head -n 240

Repository: Z-Groupware/BACKEND

Length of output: 50375


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- all classes implementing the handover ports ---'
python3 - <<'PY'
import pathlib, re
for p in pathlib.Path("src").rglob("*.java"):
    text = p.read_text(errors="ignore")
    if re.search(r'\bimplements\s+ActionReassignPort\b|\bimplements\s+OrgQueryPort\b', text):
        print(p)
        for i, line in enumerate(text.splitlines(), 1):
            if "class " in line or "implements " in line or "`@Component`" in line or "`@Profile`" in line or "`@Primary`" in line:
                print(f"{i}: {line}")
PY

printf '%s\n' '--- HandoverService finalize and constructors ---'
cat -n src/main/java/com/module06/backend/handover/application/service/HandoverService.java | sed -n '35,145p'

printf '%s\n' '--- port declarations and handover type branch ---'
cat -n src/main/java/com/module06/backend/handover/application/port/out/ActionReassignPort.java
cat -n src/main/java/com/module06/backend/handover/application/port/out/OrgQueryPort.java
rg -n -C 5 'enum HandoverType|OFFBOARDING|finalize\(' src/main/java/com/module06/backend/handover

Repository: Z-Groupware/BACKEND

Length of output: 24439


운영 환경에 실제 ActionReassignPortOrgQueryPort 구현 빈을 연결하십시오.

현재 운영 코드에는 pending 어댑터만 있습니다. OFFBOARDING 최종 승인 시 ActionReassignPortPendingAdapter.findHandoverableActions(Long)가 항상 UnsupportedOperationException을 발생시키므로 HandoverService.finalize가 실패합니다. 구현 연결 후 OrgQueryPortPendingAdapter.findMembers도 대체해야 합니다.

📍 Affects 3 files
  • src/main/java/com/module06/backend/handover/application/service/HandoverService.java#L120-L123 (this comment)
  • src/main/java/com/module06/backend/handover/infrastructure/adapter/ActionReassignPortPendingAdapter.java#L22-L30
  • src/main/java/com/module06/backend/handover/infrastructure/adapter/OrgQueryPortPendingAdapter.java#L26-L29
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/module06/backend/handover/application/service/HandoverService.java`
around lines 120 - 123, 연결된 운영 구현이 없어 OFFBOARDING 최종 승인 중 pending 어댑터가 예외를
발생시킵니다. HandoverService.finalize의 finalizeInsights 호출이 사용하는 실제
ActionReassignPort 구현 빈을 연결하고,
src/main/java/com/module06/backend/handover/infrastructure/adapter/ActionReassignPortPendingAdapter.java:22-30의
pending 구현을 대체하십시오. 또한
src/main/java/com/module06/backend/handover/infrastructure/adapter/OrgQueryPortPendingAdapter.java:26-29의
OrgQueryPort pending 구현도 실제 운영 구현 빈으로 교체하여 OrgQueryPort.findMembers가 정상 동작하게
하십시오.

@jongjunn jongjunn self-assigned this Aug 5, 2026
@jongjunn jongjunn added documentation Improvements or additions to documentation enhancement New feature or request labels Aug 5, 2026
@jongjunn jongjunn added this to Z Aug 5, 2026
@github-project-automation github-project-automation Bot moved this to Todo in Z Aug 5, 2026
@jongjunn jongjunn added this to the [M1] BE 기능 개발 milestone Aug 5, 2026
@jongjunn
jongjunn requested a review from mosungjin August 5, 2026 09:00
…정 삭제순서·직렬화 원인 보존

- startAt이 null이면 finalize 정렬(Comparator.comparing)에서 NPE → nullsLast/nullsFirst 적용
- 회의 토픽 topicId/parentTopicId가 D→E 경계에서 null로 소실 → 스냅샷/결과/포트 전 계층 전달 복원 (CONTEXT_TIMELINE 계층 복원 가능)
- HandoverServiceTest: OFFBOARDING은 finalizeInsights 1회 호출, VACATION은 미상호작용 검증 추가
- replaceAllForHandover 재확정 시 파생 삭제가 삽입 뒤로 밀려 유니크 제약 위반 가능 → @Modifying 벌크 삭제(flush/clear)로 순서 강제
- 직렬화 실패 시 JsonProcessingException 원인이 소실 → BusinessException(cause) 오버로드 추가해 원인 보존

pending 어댑터(ActionReassignPort/OrgQueryPort) 실구현 연결은 의도된 계약 대기(fail-fast) 설계라 스코프 제외.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/test/java/com/module06/backend/meeting/application/service/MeetingQueryServiceTest.java (1)

201-205: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

parentTopicId 전파를 두 매핑 경계에서 검증하세요.

현재 두 테스트는 null 부모 토픽만 사용합니다. non-null 부모 토픽 매핑이 삭제되어도 테스트가 통과합니다.

  • src/test/java/com/module06/backend/meeting/application/service/MeetingQueryServiceTest.java#L201-L205: 부모 ID가 있는 SUB fixture를 추가하고 MeetingTopicResult.parentTopicId()를 검증하세요.
  • src/test/java/com/module06/backend/meeting/infrastructure/persistence/adapter/MeetingQueryPersistenceAdapterTest.java#L187-L189: MAIN 토픽 ID를 SUB 토픽의 부모 ID로 저장하고 MeetingTopicSnapshot.parentTopicId()를 검증하세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/module06/backend/meeting/application/service/MeetingQueryServiceTest.java`
around lines 201 - 205, 두 매핑 경계에서 non-null parentTopicId 전파를 검증하도록 테스트를 보강하세요.
src/test/java/com/module06/backend/meeting/application/service/MeetingQueryServiceTest.java
201-205의 SUB fixture에 부모 ID를 설정하고 MeetingTopicResult.parentTopicId()를 검증하세요.
src/test/java/com/module06/backend/meeting/infrastructure/persistence/adapter/MeetingQueryPersistenceAdapterTest.java
187-189에서는 MAIN 토픽 ID를 SUB 토픽의 부모 ID로 저장하고 MeetingTopicSnapshot.parentTopicId()가
이를 반환하는지 검증하세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@src/test/java/com/module06/backend/meeting/application/service/MeetingQueryServiceTest.java`:
- Around line 201-205: 두 매핑 경계에서 non-null parentTopicId 전파를 검증하도록 테스트를 보강하세요.
src/test/java/com/module06/backend/meeting/application/service/MeetingQueryServiceTest.java
201-205의 SUB fixture에 부모 ID를 설정하고 MeetingTopicResult.parentTopicId()를 검증하세요.
src/test/java/com/module06/backend/meeting/infrastructure/persistence/adapter/MeetingQueryPersistenceAdapterTest.java
187-189에서는 MAIN 토픽 ID를 SUB 토픽의 부모 ID로 저장하고 MeetingTopicSnapshot.parentTopicId()가
이를 반환하는지 검증하세요.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 22b0de01-770d-4e5f-be47-9eade69ec076

📥 Commits

Reviewing files that changed from the base of the PR and between c3a913c and 52e2fc8.

📒 Files selected for processing (12)
  • src/main/java/com/module06/backend/global/exception/BusinessException.java
  • src/main/java/com/module06/backend/handover/application/service/HandoverInsightFinalizeService.java
  • src/main/java/com/module06/backend/handover/infrastructure/adapter/MeetingQueryPortDelegatingAdapter.java
  • src/main/java/com/module06/backend/handover/infrastructure/persistence/HandoverInsightJpaRepository.java
  • src/main/java/com/module06/backend/meeting/application/result/MeetingTopicResult.java
  • src/main/java/com/module06/backend/meeting/application/service/MeetingQueryService.java
  • src/main/java/com/module06/backend/meeting/domain/repository/MeetingQueryRepository.java
  • src/main/java/com/module06/backend/meeting/infrastructure/persistence/adapter/MeetingQueryPersistenceAdapter.java
  • src/test/java/com/module06/backend/handover/application/service/HandoverServiceTest.java
  • src/test/java/com/module06/backend/handover/infrastructure/adapter/MeetingQueryPortDelegatingAdapterTest.java
  • src/test/java/com/module06/backend/meeting/application/service/MeetingQueryServiceTest.java
  • src/test/java/com/module06/backend/meeting/infrastructure/persistence/adapter/MeetingQueryPersistenceAdapterTest.java
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/test/java/com/module06/backend/handover/infrastructure/adapter/MeetingQueryPortDelegatingAdapterTest.java
  • src/test/java/com/module06/backend/handover/application/service/HandoverServiceTest.java
  • src/main/java/com/module06/backend/handover/infrastructure/adapter/MeetingQueryPortDelegatingAdapter.java

@jongjunn
jongjunn force-pushed the feat/mo-handover-insight branch from 52e2fc8 to c3a913c Compare August 5, 2026 09:25

@mosungjin mosungjin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

확인했습니다.

CodeRabbit nitpick(벌크 @Modifying @query 삭제)이 팀 Gate1(신규 @query 금지)에 걸림.
handover_insight엔 유니크 제약이 없어(PK=auto id, 나머지 일반 인덱스) 삽입-삭제 순서로
인한 제약 위반이 발생하지 않으므로, 해당 nitpick을 철회하고 원래 파생 deleteByHandoverId로 되돌림.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@mosungjin mosungjin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다시 한번 확인했습니다

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants