- WBS/TEAL 화면 실제 구현 (TreeTable, FileUpload, 버전관리) - 시수이력/결재이력 화면 구현 (DataTable, Filter, Timeline) - 비밀번호변경 화면 추가 - 로그인 snake_case 응답 매핑 수정 - Vite 프록시 8081 포트 수정 - auth guard에서 fetchMe 자동 호출 - V108 샘플 데이터 (10명 사용자, 4주 시수 215건, 결재 9건) - 배너 추가 (WBX Spring) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
98 줄
3.0 KiB
Markdown
98 줄
3.0 KiB
Markdown
# 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<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)
|
|
|
|
```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() {
|
|
// 주간 미제출 사용자에게 알림
|
|
}
|
|
}
|
|
```
|