- 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>
5.4 KiB
5.4 KiB
06. 리포트 모듈
PH1-1차 리포트 (2종)
1. 프로젝트별 시수 분석 (No.82)
GET /api/reports/project-hours
?projectId=1
&from=2025-04-01
&to=2025-05-31
&groupBy=month // month, week, discipline
&format=json // json, excel
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class ReportService {
private final TimesheetEntryRepository entryRepository;
public ProjectHoursReport getProjectHoursReport(ProjectHoursFilter filter) {
// QueryDSL 동적 쿼리
QTimesheetEntry e = QTimesheetEntry.timesheetEntry;
QTimesheet ts = QTimesheet.timesheet;
QUser u = QUser.user;
var query = queryFactory
.select(Projections.constructor(ProjectHoursRow.class,
e.epcProject.projectCode,
e.epcProject.name,
u.discipline,
e.entryDate,
e.hours.sum()
))
.from(e)
.join(e.timesheet, ts)
.join(ts.user, u)
.where(
ts.status.eq(TimesheetStatus.APPROVED),
epcProjectIdEq(filter.getProjectId()),
entryDateBetween(filter.getFrom(), filter.getTo())
)
.groupBy(e.epcProject.projectCode, e.epcProject.name,
u.discipline, e.entryDate);
List<ProjectHoursRow> rows = query.fetch();
return ProjectHoursReport.builder()
.filter(filter)
.rows(rows)
.totalHours(rows.stream()
.map(ProjectHoursRow::getHours)
.reduce(BigDecimal.ZERO, BigDecimal::add))
.generatedAt(LocalDateTime.now())
.build();
}
}
2. WBS Level별 시수 분석 (No.83)
GET /api/reports/wbs-hours
?projectId=1
&wbsLevel=3 // 1~5
&from=2025-04-01
&to=2025-05-31
&format=json
public WbsHoursReport getWbsHoursReport(WbsHoursFilter filter) {
var rows = queryFactory
.select(Projections.constructor(WbsHoursRow.class,
cw.wbsCode,
cw.name,
cw.level,
cw.discipline,
e.hours.sum(),
ts.user.countDistinct()
))
.from(e)
.join(e.timesheet, ts)
.join(e.canonicalWbs, cw)
.where(
ts.status.eq(TimesheetStatus.APPROVED),
cw.projectId.eq(filter.getProjectId()),
cw.level.eq(filter.getWbsLevel()),
entryDateBetween(filter.getFrom(), filter.getTo())
)
.groupBy(cw.wbsCode, cw.name, cw.level, cw.discipline)
.orderBy(cw.wbsCode.asc())
.fetch();
return WbsHoursReport.builder()
.filter(filter)
.rows(rows)
.build();
}
PH1-2차 리포트 (2종)
3. Phase별 시수 비율 (No.85)
GET /api/reports/phase-ratio?projectId=1&from=...&to=...
4. Non-Project 시수 비율 (No.86)
GET /api/reports/np-ratio?department=Engineering&from=...&to=...
Excel Export
@Service
public class ReportExcelExporter {
public byte[] exportProjectHours(ProjectHoursReport report) {
try (Workbook wb = new XSSFWorkbook()) {
Sheet sheet = wb.createSheet("프로젝트별 시수");
// 헤더
CellStyle headerStyle = createHeaderStyle(wb);
Row header = sheet.createRow(0);
String[] headers = {"프로젝트코드", "프로젝트명", "Discipline", "날짜", "시수(h)"};
for (int i = 0; i < headers.length; i++) {
Cell cell = header.createCell(i);
cell.setCellValue(headers[i]);
cell.setCellStyle(headerStyle);
}
// 데이터
int rowIdx = 1;
for (ProjectHoursRow row : report.getRows()) {
Row r = sheet.createRow(rowIdx++);
r.createCell(0).setCellValue(row.getProjectCode());
r.createCell(1).setCellValue(row.getProjectName());
r.createCell(2).setCellValue(row.getDiscipline());
r.createCell(3).setCellValue(row.getDate().toString());
r.createCell(4).setCellValue(row.getHours().doubleValue());
}
// 합계 행
Row totalRow = sheet.createRow(rowIdx);
totalRow.createCell(3).setCellValue("합계");
totalRow.createCell(4).setCellValue(report.getTotalHours().doubleValue());
// Auto-size
for (int i = 0; i < headers.length; i++) sheet.autoSizeColumn(i);
ByteArrayOutputStream out = new ByteArrayOutputStream();
wb.write(out);
return out.toByteArray();
}
}
}
REST API (리포트)
# PH1-1차
GET /api/reports/project-hours 프로젝트별 시수 분석
GET /api/reports/project-hours/export Excel 다운로드
GET /api/reports/wbs-hours WBS Level별 시수 분석
GET /api/reports/wbs-hours/export Excel 다운로드
# PH1-2차
GET /api/reports/phase-ratio Phase별 시수 비율
GET /api/reports/np-ratio Non-Project 시수 비율
GET /api/reports/wbs-version-history WBS 버전 이력 조회
# 공통 파라미터
# projectId - 프로젝트 ID (필수/선택)
# from, to - 기간 (ISO 8601)
# groupBy - 집계 기준 (month, week, discipline)
# format - 응답 형식 (json, excel)