feat: 다수 기능 개선 - 순찰, 출근, 작업분석, 모바일 UI 등
- 순찰/점검 기능 개선 (zone-detail 페이지 추가) - 출근/근태 시스템 개선 (연차 조회, 근무현황) - 작업분석 대분류 그룹화 및 마이그레이션 스크립트 - 모바일 네비게이션 UI 추가 - NAS 배포 도구 및 문서 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* Work Analysis Service
|
||||
*
|
||||
* Handles complex business logic and data aggregation for detailed work analysis reports
|
||||
*
|
||||
* @author TK-FB-Project
|
||||
* @since 2025-12-19
|
||||
*/
|
||||
|
||||
const { getDb } = require('../dbPool');
|
||||
const WorkAnalysis = require('../models/WorkAnalysis');
|
||||
const logger = require('../utils/logger');
|
||||
const { DatabaseError } = require('../utils/errors');
|
||||
|
||||
class WorkAnalysisService {
|
||||
constructor() {
|
||||
this.db = null;
|
||||
this.model = null;
|
||||
}
|
||||
|
||||
async init() {
|
||||
if (!this.db) {
|
||||
this.db = await getDb();
|
||||
this.model = new WorkAnalysis(this.db);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 프로젝트별-작업별 시간 분석 및 집계 (Service Layer logic)
|
||||
*/
|
||||
async getProjectWorkTypeAnalysis(startDate, endDate) {
|
||||
await this.init();
|
||||
|
||||
try {
|
||||
// 1. Get Raw Data from Model
|
||||
const rawData = await this.model.getProjectWorkTypeRawData(startDate, endDate);
|
||||
|
||||
// 2. Process and Group Data
|
||||
const groupedData = {};
|
||||
|
||||
rawData.forEach(row => {
|
||||
const projectKey = `${row.project_id}_${row.project_name}`;
|
||||
|
||||
if (!groupedData[projectKey]) {
|
||||
groupedData[projectKey] = {
|
||||
project_id: row.project_id,
|
||||
project_name: row.project_name,
|
||||
job_no: row.job_no,
|
||||
total_project_hours: 0,
|
||||
total_regular_hours: 0,
|
||||
total_error_hours: 0,
|
||||
work_types: []
|
||||
};
|
||||
}
|
||||
|
||||
// Project Totals Accumulation
|
||||
groupedData[projectKey].total_project_hours += parseFloat(row.total_hours);
|
||||
groupedData[projectKey].total_regular_hours += parseFloat(row.regular_hours);
|
||||
groupedData[projectKey].total_error_hours += parseFloat(row.error_hours);
|
||||
|
||||
// Add WorkType Entry
|
||||
groupedData[projectKey].work_types.push({
|
||||
work_type_id: row.work_type_id,
|
||||
work_type_name: row.work_type_name,
|
||||
total_hours: parseFloat(row.total_hours),
|
||||
regular_hours: parseFloat(row.regular_hours),
|
||||
error_hours: parseFloat(row.error_hours),
|
||||
total_reports: row.total_reports,
|
||||
regular_reports: row.regular_reports,
|
||||
error_reports: row.error_reports,
|
||||
error_rate_percent: parseFloat(row.error_rate_percent) || 0
|
||||
});
|
||||
});
|
||||
|
||||
// 3. Calculate Project Error Rates
|
||||
Object.values(groupedData).forEach(project => {
|
||||
project.project_error_rate = project.total_project_hours > 0
|
||||
? Math.round((project.total_error_hours / project.total_project_hours) * 100 * 100) / 100
|
||||
: 0;
|
||||
});
|
||||
|
||||
// 4. Calculate Grand Totals
|
||||
const totalStats = {
|
||||
total_projects: Object.keys(groupedData).length,
|
||||
total_work_types: new Set(rawData.map(r => r.work_type_id)).size,
|
||||
grand_total_hours: Object.values(groupedData).reduce((sum, p) => sum + p.total_project_hours, 0),
|
||||
grand_regular_hours: Object.values(groupedData).reduce((sum, p) => sum + p.total_regular_hours, 0),
|
||||
grand_error_hours: Object.values(groupedData).reduce((sum, p) => sum + p.total_error_hours, 0)
|
||||
};
|
||||
|
||||
totalStats.grand_error_rate = totalStats.grand_total_hours > 0
|
||||
? Math.round((totalStats.grand_error_hours / totalStats.grand_total_hours) * 100 * 100) / 100
|
||||
: 0;
|
||||
|
||||
return {
|
||||
summary: totalStats,
|
||||
projects: Object.values(groupedData),
|
||||
period: { start: startDate, end: endDate }
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Service: 프로젝트별-작업별 시간 분석 처리 실패', { error: error.message });
|
||||
// Re-throw as DatabaseError to maintain consistency with controller expectation, or custom ServiceError
|
||||
throw new DatabaseError(`분석 데이터 처리 중 오류 발생: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new WorkAnalysisService();
|
||||
Reference in New Issue
Block a user