feat: 직무별 기준 사양(job_spec_standards) 데이터를 활용한 부족사양, 오버스펙 판정 및 등급별 부족분(구매 필요) 산출 공식 연동

This commit is contained in:
2026-06-15 14:43:53 +09:00
parent 05e23883b8
commit 6118141f6e
2 changed files with 39 additions and 15 deletions

View File

@@ -265,7 +265,14 @@ function updateDashboardData(pcs: any[], selectedDept: string) {
p._pc_score = calculatePcScoreDeductive(p.cpu, p.ram, p.gpu, p.purchase_date);
});
// 3. 전사 직무군별 평균 점수 산출
// 3. DB 기준 사양 데이터 맵핑 (state.masterData.jobSpecs 이용)
const jobSpecsMap: Record<string, number> = {};
if (state.masterData.jobSpecs) {
state.masterData.jobSpecs.forEach((s: any) => {
jobSpecsMap[s.job_name] = s.min_score;
});
}
const jobScores: Record<string, { totalScore: number; count: number; avg: number }> = {};
pcs.forEach((p: any) => {
const score = calculatePcScoreDeductive(p.cpu, p.ram, p.gpu, p.purchase_date);
@@ -332,15 +339,15 @@ function updateDashboardData(pcs: any[], selectedDept: string) {
// 직무 적정성 계산 (재직 중이고 실 사용자 매핑 자산만 검토 대상)
const job = p[ASSET_SCHEMA.USER_POSITION.key] || '미분류';
const avg = jobScores[job]?.avg || 0;
const standardScore = jobSpecsMap[job] !== undefined ? jobSpecsMap[job] : (jobScores[job]?.avg || 0);
let isUnder = false;
if (avg > 0 && job !== '재고PC') {
if (score < avg * 0.6) {
if (standardScore > 0 && job !== '재고PC') {
if (score < standardScore) {
isUnder = true;
p._spec_status = '사양 부족';
} else if (score > avg * 1.5 && !win11Incompatible) {
} else if (score > standardScore * 1.2 && !win11Incompatible) {
p._spec_status = '오버스펙';
criticalList.push(p);
overSpecCount++;
@@ -354,6 +361,8 @@ function updateDashboardData(pcs: any[], selectedDept: string) {
if (win11Incompatible) {
isUnder = true;
p._spec_status = '사양 부족';
} else {
p._spec_status = '적정';
}
}
@@ -363,11 +372,11 @@ function updateDashboardData(pcs: any[], selectedDept: string) {
// 2. 사양 부족 시 교체받아야 할 직무별 권장 목표 등급 판정
let targetGradeKey: keyof typeof matrix;
if (avg >= 85) {
if (standardScore >= 85) {
targetGradeKey = 'premium';
} else if (avg >= 70) {
} else if (standardScore >= 70) {
targetGradeKey = 'high';
} else if (avg >= 40) {
} else if (standardScore >= 40) {
targetGradeKey = 'normal';
} else {
targetGradeKey = 'entry'; // 교체 대상은 최소 보급형 사양으로 교체

View File

@@ -914,33 +914,45 @@ export function createListView(container: HTMLElement, config: ListViewConfig) {
jobScores[job].avg = jobScores[job].count > 0 ? jobScores[job].totalScore / jobScores[job].count : 0;
});
// DB 기준 사양 데이터 맵핑 (state.masterData.jobSpecs 이용)
const jobSpecsMap: Record<string, number> = {};
if (state.masterData.jobSpecs) {
state.masterData.jobSpecs.forEach((s: any) => {
jobSpecsMap[s.job_name] = s.min_score;
});
}
// 기준 대비 사양 부족/오버스펙 분류
const criticalPcList: any[] = [];
pcs.forEach((pc: any) => {
const job = pc[ASSET_SCHEMA.USER_POSITION.key] || '미분류';
const score = pc['_pc_score'];
const avg = jobScores[job].avg;
const standardScore = jobSpecsMap[job] !== undefined ? jobSpecsMap[job] : (jobScores[job]?.avg || 0);
const cpu = pc[ASSET_SCHEMA.CPU.key] || '';
const ram = pc[ASSET_SCHEMA.RAM.key] || '';
const win11Incompatible = isWindows11Incompatible(cpu, ram);
let isUnder = false;
if (avg > 0) {
if (score < avg * 0.6) {
if (standardScore > 0) {
if (score < standardScore) {
isUnder = true;
pc['_spec_status'] = '사양 부족';
} else if (score > avg * 1.5 && !win11Incompatible) {
} else if (score > standardScore * 1.2 && !win11Incompatible) {
pc['_spec_status'] = '오버스펙';
criticalPcList.push(pc);
} else if (win11Incompatible) {
isUnder = true;
pc['_spec_status'] = '사양 부족';
} else {
pc['_spec_status'] = '적정';
}
} else {
if (win11Incompatible) {
isUnder = true;
pc['_spec_status'] = '사양 부족';
} else {
pc['_spec_status'] = '적정';
}
}
@@ -949,12 +961,15 @@ export function createListView(container: HTMLElement, config: ListViewConfig) {
}
});
// 정렬: 직무 평균 대비 사양 부족이 심한 순(비율이 낮은 순)으로 정렬
// 정렬: 기준 점수 대비 사양 부족이 심한 순(비율이 낮은 순)으로 정렬
criticalPcList.sort((a: any, b: any) => {
const jobA = a[ASSET_SCHEMA.USER_POSITION.key] || '미분류';
const jobB = b[ASSET_SCHEMA.USER_POSITION.key] || '미분류';
const ratioA = jobScores[jobA].avg > 0 ? a['_pc_score'] / jobScores[jobA].avg : 1;
const ratioB = jobScores[jobB].avg > 0 ? b['_pc_score'] / jobScores[jobB].avg : 1;
const stdA = jobSpecsMap[jobA] !== undefined ? jobSpecsMap[jobA] : (jobScores[jobA]?.avg || 0);
const stdB = jobSpecsMap[jobB] !== undefined ? jobSpecsMap[jobB] : (jobScores[jobB]?.avg || 0);
const ratioA = stdA > 0 ? a['_pc_score'] / stdA : 1;
const ratioB = stdB > 0 ? b['_pc_score'] / stdB : 1;
return ratioA - ratioB;
});