Skip to content

feat: 이벤트 기반 알림 생성 로직 - #125

Merged
chaeliki merged 43 commits into
mainfrom
feat/15-notification-generation
Aug 12, 2026
Merged

feat: 이벤트 기반 알림 생성 로직#125
chaeliki merged 43 commits into
mainfrom
feat/15-notification-generation

Conversation

@chaeliki

@chaeliki chaeliki commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

왜 필요한가요?

Closes #15

PR #123 에 이어서, 실제 알림 생성 로직을 구현합니다.

완료된 범위

이벤트 기반 (4종)

  • Agent 분석 완료: TaskCreated(source=AI_CANDIDATE) 이벤트 구독
  • 승인 요청 도착: ApprovalService.requestApproval()에
    ApprovalRequested 이벤트 발행 추가
  • 문서 제출 완료: WorkerResponseService.submit()에서
    DOCUMENT_SUBMITTED 처리 완료 후 WorkerResponseSubmitted 이벤트 발행
  • 문서 보완 필요: TaskWorkflowService.updateChecklistItem()에서
    NEEDS_INFO 전이 시 TaskNeedsInfo 이벤트 발행

배치 기반 (1종)

  • 마감 임박: DueSoonNotificationScheduler(@scheduled, 매일 새벽 3시 KST)가
    전체 회사를 순회하며 7일 이내 마감인 열린 Task에 알림을 생성합니다.

결정 사항

  • 모든 알림은 이벤트를 트리거한 actor(요청자/제출확인자/재검토자)
    또는 Task 생성자(createdBy) 본인에게 보내도록 했습니다.
  • "문서 보완 필요"는 updateChecklistItem()에서 체크리스트가
    미완료로 바뀌며 NEEDS_INFO가 되는 시점으로 해석했습니다.
    update()(Task 핵심값 수정)에서도 같은 전이가 발생할 수 있는데,
    체크리스트 쪽만 반영했습니다.
  • CompanyRepository에 findAllIds()를 추가했습니다. 마감 임박 배치가
    전체 회사를 대상으로 조회해야 하는데, 타 사업장 격리(RLS)를
    지키려면 tenantDatabaseContext를 회사별로 설정하며 순회해야 해서,
    전체 회사 ID 목록이 필요했습니다.
  • 이벤트 중복 처리 방지는 기존 OutboxHandlerTransaction의
    EventConsumption 기록·검사 메커니즘을 그대로 활용했습니다
    (같은 이벤트가 같은 핸들러에 두 번 전달되지 않음).

다른 팀원 파일 수정 사항

  • ApprovalService, WorkerResponseService, TaskWorkflowService
    생성자에 DomainEventPublisher(WorkerResponseService는
    TaskRepository도 추가)를 주입했습니다.
  • RenewalExecutionIntegrationTest의 @beforeeach 정리 목록에
    event_publication, event_consumption을 추가했습니다. 이 PR에서
    추가한 이벤트 발행 코드가 실행되며 이 테스트 시나리오에서 처음으로
    event_publication 행이 쌓이게 됐고, 기존 정리 목록에 이 테이블이
    없어 company 삭제 시 FK 위반이 발생하는 것을 확인해 수정했습니다.
  • CompanyRepository 인터페이스 확장에 따라, 테스트용
    InMemoryCompanyRepository(DemoAuthSeedRunnerTest,
    DemoWorkerSeedRunnerTest)와 FakeTaskRepository
    (DashboardQueryServiceTest)에 새 메서드 구현을 추가했습니다.

어떻게 검증했나요?

  • ./gradlew clean test - 전체 통과
  • approval, workerlink, notification, task 도메인 전체 재검증 통과
  • NotificationEventHandlerTest 6개 (이벤트별 알림 생성 검증)
  • DueSoonNotificationSchedulerTest 3개 (회사별 순회, 생성자에게
    알림, KST 날짜 계산 경계값 검증)
  • RenewalExecutionIntegrationTest 11개 전체 재통과 확인

1. 승인 요청 도착

  1. POST /tasks로 MANUAL Task 생성
  2. 체크리스트 2개 완료 처리
  3. POST /tasks/{taskId}/approval-requests 호출 -> task_status가 READY_FOR_REVIEW로 전이됨
  4. GET /notifications 호출 -> 알림이 실제로 생성된 것을 확인

{
"title": "승인 요청이 도착했습니다: 실제 알림 테스트용 업무",
"route": "/tasks/{taskId}",
"target_id": "{taskId}",
"read": false
}

  • target_id와 route가 정확히 일치함을 확인했습니다
  • POST /notifications/{id}/read 호출 -> 204, 목록 재조회 시
    read: true, unread_count: 0으로 정확히 반영됨을 확인했습니다.
  • 같은 읽음 요청을 한 번 더 호출해도 여전히 204를 반환하는
    멱등성을 확인했습니다..

2. 문서 보완 필요

  1. 승인된(APPROVED) Task의 체크리스트 항목 하나를 다시
    미완료(completed: false)로 변경
  2. task_status가 NEEDS_INFO로 전이됨을 확인
  3. GET /notifications 호출 -> 알림이 실제로 생성된 것을 확인

{
"title": "문서 보완이 필요합니다: 실제 알림 테스트용 업무",
"route": "/tasks/{taskId}",
"target_id": "{taskId}",
"read": false
}

  • 이전에 생성된 "승인 요청 도착" 알림(read: true)이 그대로
    유지되고, 새 알림이 occurred_at 기준 최신순으로 앞에
    추가됨을 확인했습니다.

3. 문서 제출 완료

  1. 체크리스트를 다시 완료 처리하고 재승인(approval-requests ->
    approve)하여 task_status를 APPROVED로 전환
  2. POST /tasks/{taskId}/worker-link 호출(Idempotency-Key 헤더 필요)
    -> worker_link_token 발급
  3. POST /public/worker-links/{token}/responses 호출로 근로자가
    response_type: DOCUMENT_SUBMITTED 응답을 제출
  4. GET /notifications 호출 -> 알림이 실제로 생성된 것을 확인

{
"title": "문서 제출이 완료됐습니다: 실제 알림 테스트용 업무",
"route": "/tasks/{taskId}",
"target_id": "{taskId}",
"read": false
}

  • 같은 Task에 대해 "승인 요청 도착"과 "문서 제출 완료" 알림
    2건이 모두 정확히 쌓이고, unread_count도 2로 정확히
    집계됨을 확인했습니다.

추가 개선 사항

DueSoonCompanyNotifier라는
별도 Service로 분리하고 @transactional(propagation = REQUIRES_NEW)를
적용했습니다.

이 수정이 실제로 문제를 해결하는지, Mock이 아닌 실제 Spring
컨텍스트·트랜잭션 환경에서 검증하기 위해
DueSoonNotificationSchedulerIntegrationTest(@SpringBootTest)를
추가했습니다. 스케줄러를 직접 호출한 뒤 실제 DB를 조회한 결과입니다:

=== DueSoonNotificationScheduler 실제 생성 결과 ===
title: 마감이 임박했습니다: 마감임박테스트업무
user_id (Task 생성자): 58000000-0000-0000-0000-000000000001
expected creator_id : 58000000-0000-0000-0000-000000000001

  • 예외 없이 통과했습니다(트랜잭션 오류 재현되지 않음).
  • 알림이 실제로 생성되고, title에 "마감이 임박했습니다" 문구와 Task 제목이 정확히 포함됨을 확인했습니다.

chaeliki and others added 30 commits August 8, 2026 01:51
# Conflicts:
#	src/main/java/com/fowoco/server/dashboard/application/DashboardQueryService.java
@chaeliki
chaeliki marked this pull request as ready for review August 11, 2026 16:49
@chaeliki
chaeliki requested a review from hywznn August 11, 2026 17:04
@chaeliki
chaeliki merged commit 6da718e into main Aug 12, 2026
4 checks passed
@chaeliki
chaeliki deleted the feat/15-notification-generation branch August 12, 2026 00:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Dashboard] 오늘 업무 대시보드 요약 API 구현

2 participants