# 05. 결재 핸들러 구현 > **전제**: wbx-spring 결재 엔진(`ApprovalHandler` interface, `ApprovalHandlerRegistry`, > `UnifiedApprovalController`, `BaseApprovalLine`, `ApprovalCompletedEvent`)이 제공됨. > > WTM은 **핸들러만 구현**하면 결재 API가 자동으로 활성화됩니다. ## WTM이 구현하는 핸들러 ```java @Component @RequiredArgsConstructor public class TimesheetApprovalHandler implements ApprovalHandler { @Override public String getTypeKey() { return "timesheet"; } @Override public String getTypeDisplay() { return "시수 결재"; } @Override @Transactional public ApprovalResult approve(Long lineId, Long approverId, String comment) { TtApprovalLine line = lineRepository.findById(lineId).orElseThrow(); line.approve(); // 다음 결재자 확인 Optional next = lineRepository .findNextPending(line.getApprovalId(), line.getApprovalOrder()); Timesheet ts = line.getApproval().getTimesheet(); if (next.isPresent()) { ts.setStatus(TimesheetStatus.DL_APPROVED); notificationService.sendToUser(next.get().getApproverId(), NotificationDto.approvalRequest("시수 결재 요청", ts)); } else { ts.setStatus(TimesheetStatus.APPROVED); line.getApproval().complete(); eventPublisher.publishEvent(new ApprovalCompletedEvent( "timesheet", ts.getId(), approverId, line.getApproval())); } return ApprovalResult.success("승인 완료"); } @Override @Transactional public ApprovalResult reject(Long lineId, Long approverId, String comment) { TtApprovalLine line = lineRepository.findById(lineId).orElseThrow(); line.reject(); line.setComment(comment); line.getApproval().getTimesheet().setStatus(TimesheetStatus.REJECTED); return ApprovalResult.success("반려 완료"); } // getApprovalHistory(), getPending() 구현 ... } ``` ## 결재 흐름 (User → DL → PM) ``` User 시수 제출 ▼ TimesheetService.submit() ├── Timesheet.status = SUBMITTED ├── TtApprovalLine #1 (DL) + #2 (PM) 생성 └── DL에게 SSE 알림 (wbx-spring notificationService) ▼ DL 승인 → POST /api/wtm/approvals/unified/action/timesheet/{lineId}/approve ├── wbx-spring UnifiedApprovalController → TimesheetApprovalHandler └── PM에게 SSE 알림 ▼ PM 승인 → 최종 ├── ApprovalCompletedEvent 발행 └── User에게 승인 완료 알림 ``` ## 미완료 Timesheet 리마인더 (No.70) ```java @Component public class TimesheetReminderScheduler { private final SseNotificationService notificationService; // wbx-spring 제공 @Scheduled(cron = "0 0 17 * * MON-FRI") public void dailyReminder() { // 당일 미입력 사용자에게 알림 } @Scheduled(cron = "0 0 10 * * FRI") public void weeklySubmitReminder() { // 주간 미제출 사용자에게 알림 } } ```