파일
wbx-spring/HanwhaOCN/wtmgr/05-approval-handlers.md
accura0117 783865266b docs: 한화오션 WTM 프로젝트 계획서 추가 (00~14)
- 00~11: WTM 시수관리 시스템 설계 문서 (아키텍처, DB스키마, API스펙 등)
- 12: BE 멀티프로젝트 플랫폼 구성 계획 (wbx-spring-core 라이브러리 전환)
- 13: FE Vue3+PrimeVue4 모듈 기반 구조 계획
- 14: 레이아웃 표준 및 디자인 시스템 (반응형, 하드코딩 제거)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 19:52:15 +09:00

3.0 KiB

05. 결재 핸들러 구현

전제: wbx-spring 결재 엔진(ApprovalHandler interface, ApprovalHandlerRegistry, UnifiedApprovalController, BaseApprovalLine, ApprovalCompletedEvent)이 제공됨.

WTM은 핸들러만 구현하면 결재 API가 자동으로 활성화됩니다.

WTM이 구현하는 핸들러

@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<TtApprovalLine> 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)

@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() {
        // 주간 미제출 사용자에게 알림
    }
}