Skip to content

[FEATURE] 외주 스케줄러 및 이메일 구현#60

Open
fervovita wants to merge 23 commits into
devfrom
feat/#54-commission-scheduler
Open

[FEATURE] 외주 스케줄러 및 이메일 구현#60
fervovita wants to merge 23 commits into
devfrom
feat/#54-commission-scheduler

Conversation

@fervovita

@fervovita fervovita commented Jun 27, 2026

Copy link
Copy Markdown
Collaborator

🚀 Related issue

Closes #54

#️⃣ Summary

  • 외주 마감 자동 처리 스케줄러와 Outbox 패턴 기반 이메일 발송 시스템을 도입했습니다.

외주 마감 처리는 자정에 일괄적으로 일어나지만 사용자가 자정에 메일을 받으면 UX적으로 안 좋을 것이다라고 판단해, 메일은 아침 시간대에 발송하도록 분리할 필요가 있어 Outbox 패턴을 도입했습니다.
Message Queue 도입도 고려했으나, 현재 규모에서는 인프라 비용과 운영 복잡도가 과하다고 판단하여 DB 기반 Outbox + 스케줄러 방식을 채택했습니다.

🔧 Changes

  • Outbox 패턴 기반 이메일 발송 시스템 도입 (외주 마감 자정 처리와 메일 아침 발송 시점 분리)
  • NotificationOutbox 엔티티 + Map -> JSON 컨버터 추가
  • 이메일 발송 동기(send) / 비동기(sendAsync) 메서드 분리
  • applicationDeadline 자동 처리 스케줄러 (지원자 0/미달/충족 케이스별 취소·환불·매칭)
  • firstDraftDeadline 자동 처리 스케줄러 (시안 0/일부 미제출 케이스별 취소·부분 환불)
  • finalDeadline 자동 처리 스케줄러 (강사 미선택 취소 / 수정 진행 중 자동 최종 확정)
  • PayoutRequestedEvent 도입 (어드민 정산 요청 — 자동/수동 확정, 취소 환불, 미선택 정산 모든 시점 재사용)
  • CommissionCompletedEvent 도입 (외주 확정 시 강사/디자이너 알림)
  • DraftSelectedEvent 도입 (강사 시안 선택 시 선택/미선택 디자이너 알림)
  • 가격 정책을 CommissionPricePolicy로 캡슐화 (기본금 / 인센티브 / 미달 환불 / 정산 금액)
  • Payment 환불 요청 도메인 메서드 추가 (markFullRefundRequested, markPartialRefundRequested)
  • 디자이너 경험치 획득 로직 캡슐화 (gainDraftSelectedReward, gainCommissionCompletedReward)
  • ExpReward.REVISION -> COMMISSION_COMPLETED 이름 변경 (외주 완료 보상 의미 명확화)

📸 Test Evidence

  • swagger나 postman 결과를 캡쳐하여 첨부

💬 Reviewer Notes

  • 어드민 정산 메일에는 계좌 정보를 포함하지 않습니다. 어드민이 별도 API로 디자이너 ID로 계좌 조회 후 송금하는 흐름입니다.
  • Payment에서 구현을 간단히 하기 위해 부분 환불시 amount를 차감하는 식으로 구현했습니다.
  • "모든 시안 제출 시 강사 즉시 알림"은 디자이너 시안 제출 트리거가 추가될 때 다룰 예정입니다.
  • "수정 요청/수정 응답 제출시 즉시 알림"은 수정 응답 트리거가 추가될 때 다룰 예정입니다.

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features
    • 외주 진행 단계에 맞춘 자동 처리와 알림 메일 발송이 추가되었습니다.
    • 시안 선택/미선택, 1차 시안 미제출, 최종 확정/자동 취소, 환불·정산 요청 안내 메일 템플릿이 제공됩니다.
    • 환불 및 정산 요청이 자동으로 기록·처리됩니다.
  • Bug Fixes
    • 이메일 발송이 안정적으로 비동기 처리되고, 예약 발송도 순차 처리되도록 개선되었습니다.
    • 마감 시 상태 전환과 환불 금액 산정 로직이 보강되었습니다.
  • Style
    • 이메일 알림 화면과 문구가 전반적으로 정리되었습니다.

fervovita added 21 commits June 25, 2026 00:08
- 1차 시안 마감 로직을 위한 FirstDraftDeadlineProcessor 구현
- 1차 시안 마감 관련 이벤트 및 이벤트 리스너 작성
- 1차 시안 미제출자 알림 및 환불 요청 이메일 발송 로직 추가
- CommissionScheduler에 1차 시안 마감 스케줄러 설정
- KST 기반 날짜 및 시간 처리 로직 반영

Refs: #54
@fervovita fervovita self-assigned this Jun 27, 2026
@fervovita fervovita requested a review from Jong0128 as a code owner June 27, 2026 02:14
@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: 363f0d93-1ece-4c58-a8d9-e6724cec72b9

📥 Commits

Reviewing files that changed from the base of the PR and between aba8ecb and 17d0252.

📒 Files selected for processing (2)
  • src/main/java/ditda/backend/domain/commission/core/processor/FinalDeadlineProcessor.java
  • src/main/java/ditda/backend/domain/payment/entity/Payment.java
💤 Files with no reviewable changes (1)
  • src/main/java/ditda/backend/domain/commission/core/processor/FinalDeadlineProcessor.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/ditda/backend/domain/payment/entity/Payment.java

📝 Walkthrough

Walkthrough

외주 지원, 1차 시안, 최종 마감 처리와 관련된 스케줄러, 상태 전이, 환불·정산 계산, 이벤트 발행, outbox 저장, 이메일 템플릿이 추가되었다.

Changes

외주 마감 및 이메일 흐름

Layer / File(s) Summary
이메일 전송 분리
src/main/java/ditda/backend/global/email/EmailSender.java, src/main/java/ditda/backend/domain/auth/notification/*.java, src/main/java/ditda/backend/domain/payment/notification/DepositMailer.java
EmailSendersendsendAsync로 분리되고, 기존 인증·입금 메일 발신자가 sendAsync를 호출하도록 바뀌었다.
아웃박스 저장과 발송
src/main/java/ditda/backend/global/converter/MapToJsonConverter.java, src/main/java/ditda/backend/global/email/OutboxStatus.java, src/main/java/ditda/backend/global/email/NotificationOutbox.java, src/main/java/ditda/backend/global/email/NotificationOutboxRepository.java, src/main/java/ditda/backend/global/email/NotificationOutboxScheduler.java
NotificationOutbox가 JSON 변수와 상태/재시도/전송 시각을 저장하고, pending 레코드를 배치 조회해 발송하는 스케줄러가 추가되었다.
지원자와 외주 상태
src/main/java/ditda/backend/domain/commission/application/entity/CommissionApplication.java, src/main/java/ditda/backend/domain/commission/application/entity/enums/ApplicationStatus.java, src/main/java/ditda/backend/domain/commission/application/exception/ApplicationErrorCode.java, src/main/java/ditda/backend/domain/commission/application/repository/CommissionApplicationRepository.java, src/main/java/ditda/backend/domain/commission/application/service/ApplicationService.java, src/main/java/ditda/backend/domain/commission/core/entity/Commission.java
지원자 미제출/배정 판정, 외주 상태 전이, fetch join 조회, 리스트 기반 선택 처리가 추가되었다.
보상과 환불 계산
src/main/java/ditda/backend/domain/designer/entity/Designer.java, src/main/java/ditda/backend/domain/designer/entity/enums/ExpReward.java, src/main/java/ditda/backend/domain/payment/entity/Payment.java, src/main/java/ditda/backend/domain/payment/exception/PaymentErrorCode.java, src/main/java/ditda/backend/domain/payment/service/PaymentService.java, src/main/java/ditda/backend/domain/commission/core/policy/CommissionPricePolicy.java
디자이너 EXP 보상 메서드, 결제 환불 상태 전이, 환불 서비스, 커미션 보상·환불 금액 계산 정책이 추가되었다.
마감 이벤트 payload
src/main/java/ditda/backend/domain/commission/core/event/ApplicationDeadlineClosedEvent.java, src/main/java/ditda/backend/domain/commission/core/event/FirstDraftDeadlineClosedEvent.java, src/main/java/ditda/backend/domain/commission/core/event/FinalDeadlineClosedEvent.java, src/main/java/ditda/backend/domain/commission/core/event/DraftSelectedEvent.java, src/main/java/ditda/backend/domain/commission/core/event/PayoutRequestedEvent.java, src/main/java/ditda/backend/domain/commission/core/event/CommissionCompletedEvent.java
마감, 선택, 완료, 정산 요청용 record 이벤트와 nested 정보 타입이 추가되었다.
마감 스케줄링과 프로세서
src/main/java/ditda/backend/domain/commission/core/repository/CommissionRepository.java, src/main/java/ditda/backend/domain/commission/core/scheduler/CommissionScheduler.java, src/main/java/ditda/backend/domain/commission/core/service/CommissionDeadlineService.java, src/main/java/ditda/backend/domain/commission/core/processor/ApplicationDeadlineProcessor.java, src/main/java/ditda/backend/domain/commission/core/processor/FirstDraftDeadlineProcessor.java, src/main/java/ditda/backend/domain/commission/core/processor/FinalDeadlineProcessor.java
오늘 기준 마감 대상 커미션을 조회해 3종 마감 프로세서를 실행하고, 각 프로세서가 상태 전이·환불·이벤트 발행을 수행한다.
지원 마감 알림
src/main/java/ditda/backend/domain/commission/core/notification/ApplicationDeadlineClosedNotifier.java, src/main/resources/templates/email/admin-*.html, src/main/resources/templates/email/commission-cancelled.html, src/main/resources/templates/email/commission-matched-instructor.html, src/main/resources/templates/email/commission-matched-designer.html, src/main/resources/templates/email/commission-shortfall-instructor.html
지원 마감 결과에 따라 관리자 환불/정산 요청과 강사·디자이너 알림 메일이 outbox에 저장된다.
1차 시안과 최종 마감 알림
src/main/java/ditda/backend/domain/commission/core/notification/FirstDraftDeadlineClosedNotifier.java, src/main/java/ditda/backend/domain/commission/core/notification/FinalDeadlineClosedNotifier.java, src/main/resources/templates/email/first-draft-zero-instructor.html, src/main/resources/templates/email/first-draft-shortfall-instructor.html, src/main/resources/templates/email/first-draft-missed-designer.html, src/main/resources/templates/email/final-cancelled-instructor.html, src/main/resources/templates/email/final-cancelled-designer.html
1차 시안 마감의 환불/누락 알림과 최종 마감 취소 알림이 outbox에 저장된다.
수동 선택과 완료 알림
src/main/java/ditda/backend/domain/commission/draft/facade/DraftFacade.java, src/main/java/ditda/backend/domain/commission/core/notification/DraftSelectedNotifier.java, src/main/java/ditda/backend/domain/commission/core/notification/PayoutRequestedNotifier.java, src/main/java/ditda/backend/domain/commission/core/notification/CommissionCompletedNotifier.java, src/main/resources/templates/email/first-draft-selected-designer.html, src/main/resources/templates/email/first-draft-rejected-designer.html, src/main/resources/templates/email/commission-finalized-instructor.html, src/main/resources/templates/email/commission-finalized-designer.html
DraftFacade가 선택·확정 시 payout과 선택/완료 이벤트를 발행하고, 선택·정산·완료 알림이 outbox에 저장된다.

Sequence Diagram(s)

sequenceDiagram
  participant CommissionScheduler
  participant CommissionDeadlineService
  participant ApplicationDeadlineProcessor
  participant FirstDraftDeadlineProcessor
  participant FinalDeadlineProcessor

  CommissionScheduler->>CommissionDeadlineService: processApplicationDeadlines()
  CommissionDeadlineService->>ApplicationDeadlineProcessor: process(commissionId, mailScheduledAt)
  CommissionScheduler->>CommissionDeadlineService: processFirstDraftDeadlines()
  CommissionDeadlineService->>FirstDraftDeadlineProcessor: process(commissionId, mailScheduledAt)
  CommissionScheduler->>CommissionDeadlineService: processFinalDeadlines()
  CommissionDeadlineService->>FinalDeadlineProcessor: process(commissionId, mailScheduledAt)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • Ditda-Official/Ditda-Backend#38: 시안 선택/거절의 상태 전이와 ApplicationService.applySelection(...) 변경이 이번 PR의 DraftFacade 선택 흐름과 직접 연결됩니다.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.36% 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 제목이 외주 스케줄러와 이메일 구현이라는 핵심 변경을 간결하게 요약합니다.
Linked Issues check ✅ Passed 이슈 #54의 외주 마감 스케줄러 및 이메일 구현 요구를 스케줄러, Outbox, 알림 메일로 충족합니다.
Out of Scope Changes check ✅ Passed 변경 사항은 스케줄러, 알림, 환불/정산, 보상 로직 등 모두 기능 범위 안에 있습니다.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#54-commission-scheduler

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 PMD (7.25.0)
src/main/java/ditda/backend/domain/payment/entity/Payment.java

[ERROR] Cannot load ruleset .coderabbit-pmd-ruleset.xml: Cannot resolve rule/ruleset reference '.coderabbit-pmd-ruleset.xml'. Make sure the resource is a valid file or URL and is on the CLASSPATH. Use --debug (or a fine log level) to see the current classpath.
[WARN] Progressbar rendering conflicts with reporting to STDOUT. No progressbar will be shown. Try running with argument -r to output the report to a file instead.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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: 6

🧹 Nitpick comments (4)
src/main/java/ditda/backend/domain/commission/core/policy/CommissionPricePolicy.java (1)

28-31: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

정수 나눗셈 절삭 동작을 의도한 것인지 확인 바랍니다.

level3Reward * shortfallCount * 4 / 3는 정수 연산이라 level3Reward * shortfallCount가 3의 배수가 아니면 절삭됩니다. 현재 LEVEL_3 기본금(60,000)은 3의 배수라 정확히 떨어지지만, 향후 기본금 정책이 바뀌면 환불 금액에서 미세한 오차가 발생할 수 있습니다. 의도된 절삭이 아니라면 곱셈을 먼저 모두 수행한 뒤(이미 그렇게 동작) 정밀도 손실 여부를 주석으로 명시하거나 반올림 정책을 명확히 해두는 것을 권장합니다.

🤖 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/ditda/backend/domain/commission/core/policy/CommissionPricePolicy.java`
around lines 28 - 31, The calculation in
CommissionPricePolicy.calculateApplicationShortfallRefund relies on integer
arithmetic, so the existing level3Reward * shortfallCount * 4 / 3 expression may
truncate if the intermediate value is not divisible by 3. Verify whether this
truncation is intended; if not, update the calculation to make the rounding
behavior explicit by using a clearer rounding strategy or document the exact
expected precision in calculateApplicationShortfallRefund and related reward
methods like calculateDraftSubmissionReward.
src/main/java/ditda/backend/global/email/NotificationOutboxScheduler.java (1)

55-55: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

TODO: FAILED 상태 시 Discord 웹훅 알림.

발송이 3회 실패해 FAILED로 전이된 레코드에 대한 알림(웹훅) 처리가 아직 구현되지 않았습니다. 운영 가시성 측면에서 중요합니다. 구현 코드를 생성하거나 추적용 이슈를 열어 드릴까요?

🤖 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/ditda/backend/global/email/NotificationOutboxScheduler.java` at
line 55, The FAILED-state Discord webhook notification is still missing in
NotificationOutboxScheduler, so add the implementation for records that
transition to FAILED after 3 send attempts. Locate the existing status handling
in NotificationOutboxScheduler and extend it to trigger the Discord webhook when
outbox.getStatus() becomes FAILED, using the scheduler’s current notification
flow and any existing webhook/client helper methods if present. Keep the FAILED
handling isolated so it’s easy to trace and maintain alongside the current
outbox processing logic.
src/main/java/ditda/backend/global/converter/MapToJsonConverter.java (1)

34-36: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

공백 문자열(blank) DB 값 처리 누락.

isEmpty()만 검사하므로 " " 같은 공백 문자열은 파싱 경로로 진입해 역직렬화 실패로 이어집니다. isBlank()로 방어하는 것을 고려해 주세요.

♻️ 제안 변경
-		if (dbData == null || dbData.isEmpty()) {
+		if (dbData == null || dbData.isBlank()) {
 			return Map.of();
 		}
🤖 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/ditda/backend/global/converter/MapToJsonConverter.java` around
lines 34 - 36, The MapToJsonConverter null/empty guard does not cover blank
strings, so whitespace-only DB values still reach the JSON parsing path and can
fail. Update the early return in MapToJsonConverter’s conversion logic to treat
blank input the same as empty input, using the existing dbData check before
deserialization. Keep the fix localized to the converter method that currently
checks dbData.isEmpty().
src/main/java/ditda/backend/domain/commission/core/service/CommissionDeadlineService.java (1)

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

세 메서드의 중복 구조 정리 검토 (선택).

processApplicationDeadlines / processFirstDraftDeadlines / processFinalDeadlines는 today 계산 → mailScheduledAt 계산 → 조회 → for 루프 try/catch 로깅 흐름이 거의 동일합니다. 조회 함수와 프로세서 호출만 파라미터로 받는 공통 헬퍼로 추출하면 중복과 // TODO: 디스코드 웹훅 처리 누락 위험을 줄일 수 있습니다.

🤖 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/ditda/backend/domain/commission/core/service/CommissionDeadlineService.java`
around lines 33 - 103, The three deadline methods in CommissionDeadlineService
are nearly identical, so extract the shared today/mailScheduledAt lookup,
repository fetch, and try/catch loop into a common helper. Keep the specific
parts—CommissionRepository query, CommissionDeadlineProcessor call
(applicationDeadlineProcessor, firstDraftDeadlineProcessor,
finalDeadlineProcessor), and the log message—passed in as parameters or lambdas,
so the duplicated structure and TODO handling live in one place.
🤖 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/ditda/backend/domain/auth/notification/EmailVerificationMailer.java`:
- Line 20: The authentication email flow in EmailVerificationMailer now uses
EmailSender.sendAsync, which hides delivery failures from the caller. Change the
verification-code path to use the synchronous send method (or route it through a
retry-safe outbox) so exceptions propagate back to the code that issues the
verification code; keep the fix localized to EmailVerificationMailer and the
emailSender invocation used for 인증 코드 발급.

In
`@src/main/java/ditda/backend/domain/commission/core/processor/FinalDeadlineProcessor.java`:
- Around line 46-67: `FinalDeadlineProcessor.process()` already publishes
`publishFinalDeadlineClosedEvent(...)` after `applyFinalDeadline(...)`, so the
CASE1 path in `applyFinalDeadline(...)` is duplicating the same event. Remove
the extra `publishFinalDeadlineClosedEvent(...)` call from the `DRAFT_SELECTING`
branch and keep event emission centralized in `process()` so
`FinalDeadlineClosedEvent` is sent exactly once for both `applyFinalDeadline`
paths.

In
`@src/main/java/ditda/backend/domain/commission/core/processor/FirstDraftDeadlineProcessor.java`:
- Around line 92-111: The partial refund flow in handlePartialSubmission should
not run when there are no missed submissions. Add a guard before calculating
missedLevels and calling paymentService.requestPartialRefund so the refund path
is skipped when missed is empty, while still keeping
commission.startDraftSelecting() and
applicationService.markAllDraftMissed(missed) behavior intact. Use the existing
handlePartialSubmission, missed, and paymentService.requestPartialRefund symbols
to locate and adjust the logic.

In `@src/main/java/ditda/backend/domain/commission/draft/facade/DraftFacade.java`:
- Around line 68-72: The timestamp handling in DraftFacade is inconsistent
because applyDesignerSelection uses a system-default LocalDateTime now while
mail scheduling uses ZONE_KST. Update the now value in the selectDesigner flow
to use the same KST-based clock as mailScheduledAt, and keep the selection time,
response timestamp, and event scheduling aligned through the same timezone
reference.

In `@src/main/java/ditda/backend/domain/payment/entity/Payment.java`:
- Around line 83-93: The partial refund flow in
Payment.markPartialRefundRequested is not idempotent and also lacks validation
for non-positive refund amounts. Update this method to reject refundAmount <= 0,
and add a status transition or other guard so repeated invocations for the same
payment cannot subtract the amount twice. Also avoid mutating the original
amount field directly; instead, use a separate field for accumulated refunded
amount (or equivalent) so Payment keeps the original total for tracking and
audit purposes.

In `@src/main/java/ditda/backend/global/email/NotificationOutboxScheduler.java`:
- Around line 40-58: The outbox processing in
NotificationOutboxScheduler.processPendingAlerts can send the same email more
than once because findPendingScheduled returns PENDING rows without any
claim/lock, and a successful emailSender.send followed by a failed
outboxRepository.save will cause retries to resend. Update the flow to
atomically claim each NotificationOutbox before sending, such as transitioning
PENDING to SENDING with a conditional update or adding a pessimistic lock on the
pending query, and only markSent()/persist the final state after the claim is
secured.

---

Nitpick comments:
In
`@src/main/java/ditda/backend/domain/commission/core/policy/CommissionPricePolicy.java`:
- Around line 28-31: The calculation in
CommissionPricePolicy.calculateApplicationShortfallRefund relies on integer
arithmetic, so the existing level3Reward * shortfallCount * 4 / 3 expression may
truncate if the intermediate value is not divisible by 3. Verify whether this
truncation is intended; if not, update the calculation to make the rounding
behavior explicit by using a clearer rounding strategy or document the exact
expected precision in calculateApplicationShortfallRefund and related reward
methods like calculateDraftSubmissionReward.

In
`@src/main/java/ditda/backend/domain/commission/core/service/CommissionDeadlineService.java`:
- Around line 33-103: The three deadline methods in CommissionDeadlineService
are nearly identical, so extract the shared today/mailScheduledAt lookup,
repository fetch, and try/catch loop into a common helper. Keep the specific
parts—CommissionRepository query, CommissionDeadlineProcessor call
(applicationDeadlineProcessor, firstDraftDeadlineProcessor,
finalDeadlineProcessor), and the log message—passed in as parameters or lambdas,
so the duplicated structure and TODO handling live in one place.

In `@src/main/java/ditda/backend/global/converter/MapToJsonConverter.java`:
- Around line 34-36: The MapToJsonConverter null/empty guard does not cover
blank strings, so whitespace-only DB values still reach the JSON parsing path
and can fail. Update the early return in MapToJsonConverter’s conversion logic
to treat blank input the same as empty input, using the existing dbData check
before deserialization. Keep the fix localized to the converter method that
currently checks dbData.isEmpty().

In `@src/main/java/ditda/backend/global/email/NotificationOutboxScheduler.java`:
- Line 55: The FAILED-state Discord webhook notification is still missing in
NotificationOutboxScheduler, so add the implementation for records that
transition to FAILED after 3 send attempts. Locate the existing status handling
in NotificationOutboxScheduler and extend it to trigger the Discord webhook when
outbox.getStatus() becomes FAILED, using the scheduler’s current notification
flow and any existing webhook/client helper methods if present. Keep the FAILED
handling isolated so it’s easy to trace and maintain alongside the current
outbox processing logic.
🪄 Autofix (Beta)

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: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: e4a0897f-0694-4afe-81ae-c87ef3c9e231

📥 Commits

Reviewing files that changed from the base of the PR and between be70f8a and aba8ecb.

📒 Files selected for processing (55)
  • src/main/java/ditda/backend/domain/auth/notification/DesignerSignupMailer.java
  • src/main/java/ditda/backend/domain/auth/notification/EmailVerificationMailer.java
  • src/main/java/ditda/backend/domain/commission/application/entity/CommissionApplication.java
  • src/main/java/ditda/backend/domain/commission/application/entity/enums/ApplicationStatus.java
  • src/main/java/ditda/backend/domain/commission/application/exception/ApplicationErrorCode.java
  • src/main/java/ditda/backend/domain/commission/application/repository/CommissionApplicationRepository.java
  • src/main/java/ditda/backend/domain/commission/application/service/ApplicationService.java
  • src/main/java/ditda/backend/domain/commission/core/entity/Commission.java
  • src/main/java/ditda/backend/domain/commission/core/event/ApplicationDeadlineClosedEvent.java
  • src/main/java/ditda/backend/domain/commission/core/event/CommissionCompletedEvent.java
  • src/main/java/ditda/backend/domain/commission/core/event/DraftSelectedEvent.java
  • src/main/java/ditda/backend/domain/commission/core/event/FinalDeadlineClosedEvent.java
  • src/main/java/ditda/backend/domain/commission/core/event/FirstDraftDeadlineClosedEvent.java
  • src/main/java/ditda/backend/domain/commission/core/event/PayoutRequestedEvent.java
  • src/main/java/ditda/backend/domain/commission/core/notification/ApplicationDeadlineClosedNotifier.java
  • src/main/java/ditda/backend/domain/commission/core/notification/CommissionCompletedNotifier.java
  • src/main/java/ditda/backend/domain/commission/core/notification/DraftSelectedNotifier.java
  • src/main/java/ditda/backend/domain/commission/core/notification/FinalDeadlineClosedNotifier.java
  • src/main/java/ditda/backend/domain/commission/core/notification/FirstDraftDeadlineClosedNotifier.java
  • src/main/java/ditda/backend/domain/commission/core/notification/PayoutRequestedNotifier.java
  • src/main/java/ditda/backend/domain/commission/core/policy/CommissionPricePolicy.java
  • src/main/java/ditda/backend/domain/commission/core/processor/ApplicationDeadlineProcessor.java
  • src/main/java/ditda/backend/domain/commission/core/processor/FinalDeadlineProcessor.java
  • src/main/java/ditda/backend/domain/commission/core/processor/FirstDraftDeadlineProcessor.java
  • src/main/java/ditda/backend/domain/commission/core/repository/CommissionRepository.java
  • src/main/java/ditda/backend/domain/commission/core/scheduler/CommissionScheduler.java
  • src/main/java/ditda/backend/domain/commission/core/service/CommissionDeadlineService.java
  • src/main/java/ditda/backend/domain/commission/draft/facade/DraftFacade.java
  • src/main/java/ditda/backend/domain/designer/entity/Designer.java
  • src/main/java/ditda/backend/domain/designer/entity/enums/ExpReward.java
  • src/main/java/ditda/backend/domain/payment/entity/Payment.java
  • src/main/java/ditda/backend/domain/payment/exception/PaymentErrorCode.java
  • src/main/java/ditda/backend/domain/payment/notification/DepositMailer.java
  • src/main/java/ditda/backend/domain/payment/service/PaymentService.java
  • src/main/java/ditda/backend/global/converter/MapToJsonConverter.java
  • src/main/java/ditda/backend/global/email/EmailSender.java
  • src/main/java/ditda/backend/global/email/NotificationOutbox.java
  • src/main/java/ditda/backend/global/email/NotificationOutboxRepository.java
  • src/main/java/ditda/backend/global/email/NotificationOutboxScheduler.java
  • src/main/java/ditda/backend/global/email/OutboxStatus.java
  • src/main/resources/templates/email/admin-payout-request.html
  • src/main/resources/templates/email/admin-refund-request.html
  • src/main/resources/templates/email/commission-cancelled.html
  • src/main/resources/templates/email/commission-finalized-designer.html
  • src/main/resources/templates/email/commission-finalized-instructor.html
  • src/main/resources/templates/email/commission-matched-designer.html
  • src/main/resources/templates/email/commission-matched-instructor.html
  • src/main/resources/templates/email/commission-shortfall-instructor.html
  • src/main/resources/templates/email/final-cancelled-designer.html
  • src/main/resources/templates/email/final-cancelled-instructor.html
  • src/main/resources/templates/email/first-draft-missed-designer.html
  • src/main/resources/templates/email/first-draft-rejected-designer.html
  • src/main/resources/templates/email/first-draft-selected-designer.html
  • src/main/resources/templates/email/first-draft-shortfall-instructor.html
  • src/main/resources/templates/email/first-draft-zero-instructor.html

Comment thread src/main/java/ditda/backend/domain/payment/entity/Payment.java
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] 외주 스케줄러 및 이메일 구현

1 participant