temp: save local progress before merge

This commit is contained in:
2026-06-02 10:23:18 +09:00
parent bf7fb0ffe6
commit bb859dddfc
21 changed files with 13296 additions and 171 deletions

447
scratch/update_dummy_pcs.js Normal file
View File

@@ -0,0 +1,447 @@
import pkg from 'xlsx';
import * as fs from 'fs';
import * as path from 'path';
const { readFile, utils } = pkg;
// 임시 ID 생성 및 도우미 함수
const randomId = () => Math.random().toString(36).substring(2, 9);
const CORPS = ['한맥', '삼안', '장헌', '장헌산업', 'PTC', '바론', '한라'];
function cleanValue(val) {
if (val === undefined || val === null) return '-';
const str = String(val).trim();
return str === '' ? '-' : str;
}
try {
const workbook = readFile('c:/Project/HM ITAM/SampleData_PC.xlsx');
const sheet = workbook.Sheets[workbook.SheetNames[0]];
// header: 1로 읽어 2차원 배열을 획득
const rawRows = utils.sheet_to_json(sheet, { header: 1 });
// 첫 번째 행은 헤더이므로 제외
const dataRows = rawRows.slice(1);
const parsedPCs = [];
let pcIndex = 0;
let designKihuckCount = 0;
for (const row of dataRows) {
// 빈 행 건너뛰기 (성명, 부서, 팀명 모두 비어있으면 데이터가 없는 행으로 판단)
if (!row[0] && !row[1] && !row[2] && !row[3] && !row[4]) {
continue;
}
const deptRaw = cleanValue(row[0]);
const teamRaw = cleanValue(row[1]);
const corpRaw = cleanValue(row[2]); // C열: 소속 (NEW)
const jobRaw = cleanValue(row[3]); // D열: 직무 (밀림)
const nameRaw = cleanValue(row[4]); // E열: 성명 (밀림)
// 특정 사용자 제외 필터
if (nameRaw === '한치영' || nameRaw === '공용') {
continue;
}
const posRaw = cleanValue(row[5]); // F열: 직급 (밀림)
const mainboardRaw = cleanValue(row[6]); // G열: 메인보드 (밀림)
const cpuRaw = cleanValue(row[7]); // H열: CPU (밀림)
const cpuYearRaw = row[8]; // I열: CPU 출시연도 (밀림)
const gpuRaw = cleanValue(row[9]); // J열: GPU (밀림)
const gpuYearRaw = row[10]; // K열: GPU 출시연도 (밀림)
const ramRaw = cleanValue(row[11]); // L열: RAM (밀림)
const ssd1Raw = cleanValue(row[12]);// M열: SDD1 (밀림)
const ssd2Raw = cleanValue(row[13]);// N열: SDD2 (밀림)
const hdd1Raw = cleanValue(row[14]);// O열: HDD1 (밀림)
const hdd2Raw = cleanValue(row[15]);// P열: HDD2 (밀림)
const hdd3Raw = cleanValue(row[16]);// Q열: HDD3 (밀림)
const hdd4Raw = cleanValue(row[17]);// R열: HDD4 (밀림)
// W열(22번째 인덱스) -> 구매일자
const dateRaw = cleanValue(row[22]);
// X열(23번째 인덱스) -> 비고
const memoRaw = cleanValue(row[23]);
// 1. 법인 매핑 (엑셀 C열의 실제 소속 우선 사용, 없을 시 순환 지정)
const purchase_corp = corpRaw !== '-' ? corpRaw : CORPS[pcIndex % CORPS.length];
// 2. 재고PC 판단 및 상태 설정
const isStock = teamRaw === '재고PC';
const hw_status = isStock ? '창고보관' : '운영중';
// 3. 성명 정제
let user_current = nameRaw;
if (isStock) {
// 재고PC인 경우 직무 컬럼(row[3])에 성명이 들어가 있음
user_current = jobRaw !== '-' ? jobRaw : '재고장비';
}
// 4. 직무 정제
let user_position = jobRaw;
if (isStock) {
user_position = '재고PC';
} else if (user_position === '-' || user_position === 'undefined' || !user_position || ['안용주', '김민수', '심영표', '이수창A', '조병철', '윤진호', '김대영', '박정웅', '김유식'].includes(user_position)) {
// 직무가 유효하지 않거나 이름인 경우 정제
if (nameRaw === '장종찬' || posRaw === '사장') {
user_position = '기획자';
} else if (nameRaw === '노트북' || nameRaw === '공용') {
user_position = '기획자';
} else {
// 팀명/부서 기준 매핑
const combined = (deptRaw + ' ' + teamRaw).toUpperCase();
if (combined.includes('개발') || combined.includes('SOLUTION') || combined.includes('WEB') || combined.includes('ERP')) {
user_position = '개발자';
} else if (combined.includes('BIM') || combined.includes('구조') || combined.includes('설계') || combined.includes('터널') || combined.includes('상하수도') || combined.includes('수자원') || combined.includes('건설') || combined.includes('CM')) {
user_position = '엔지니어';
} else if (combined.includes('디자인') || combined.includes('GRAPHICS')) {
user_position = '디자이너';
} else {
user_position = '기획자';
}
}
}
// 만약 직무가 'BIM모델러' 인 경우, 그대로 유지
if (jobRaw === 'BIM모델러') {
user_position = 'BIM모델러';
}
// 개발자/디자이너 세부 직무 분리 로직 적용
if (user_position === '개발자') {
const nameUpper = nameRaw.trim();
const teamUpper = teamRaw.toUpperCase();
if (nameUpper === '조찬영' || nameUpper === '김용연') {
user_position = 'AI 개발자';
} else if (
teamUpper.includes('그래픽스') ||
teamUpper.includes('MODELER') ||
teamUpper.includes('HMEG') ||
teamUpper.includes('EG-BIM') ||
teamUpper.includes('GSIM') ||
teamUpper.includes('STRANA')
) {
user_position = '3D 개발자';
} else if (
teamUpper.includes('WEB') ||
teamUpper.includes('솔루션개발') ||
teamUpper.includes('ERP') ||
teamUpper.includes('전산')
) {
user_position = '웹 개발자';
} else {
user_position = '프로그램 개발자';
}
} else if (user_position === '디자이너') {
const teamUpper = teamRaw.toUpperCase();
if (teamUpper.includes('디자인셀')) {
user_position = 'UXUI 디자이너';
} else if (teamUpper.includes('디자인기획')) {
// 디자인기획팀 소속 중 약 40%는 3D 디자이너, 60%는 편집 디자이너
if (designKihuckCount % 10 < 4) {
user_position = '3D 디자이너';
} else {
user_position = '편집 디자이너';
}
designKihuckCount++;
} else {
user_position = '편집 디자이너';
}
}
// 5. 구매일자 포맷 가공 (YYYY-MM)
let purchase_date = '2022-01'; // 기본값
if (dateRaw !== '-') {
if (dateRaw.length === 6 && !isNaN(dateRaw)) {
purchase_date = `${dateRaw.substring(0, 4)}-${dateRaw.substring(4, 6)}`;
} else if (dateRaw.length === 4 && !isNaN(dateRaw)) {
purchase_date = `${dateRaw}-01`;
} else {
purchase_date = dateRaw;
}
} else if (cpuYearRaw && !isNaN(cpuYearRaw)) {
purchase_date = `${cpuYearRaw}-01`;
}
// 6. 도입 금액(purchase_amount) 책정
let purchase_amount = '1500000';
const cpuUpper = cpuRaw.toUpperCase();
const gpuUpper = gpuRaw.toUpperCase();
if (cpuUpper.includes('I9') || cpuUpper.includes('RYZEN 9') || cpuUpper.includes('RYZEN9') || gpuUpper.includes('4080') || gpuUpper.includes('4090')) {
purchase_amount = '3500000';
} else if (cpuUpper.includes('I7') || cpuUpper.includes('RYZEN 7') || cpuUpper.includes('RYZEN7') || gpuUpper.includes('3070') || gpuUpper.includes('4070') || gpuUpper.includes('A2000')) {
purchase_amount = '2200000';
} else if (cpuUpper.includes('I5') || cpuUpper.includes('RYZEN 5') || cpuUpper.includes('RYZEN5') || gpuUpper.includes('3060') || gpuUpper.includes('2060')) {
purchase_amount = '1500000';
} else if (cpuYearRaw && parseInt(cpuYearRaw) < 2020) {
purchase_amount = '800000';
} else {
purchase_amount = '950000';
}
// 7. MAC 주소 생성 (16진수 포맷)
const mac_address = `00:1A:2B:3C:4D:${pcIndex.toString(16).toUpperCase().padStart(2, '0')}`;
parsedPCs.push({
id: randomId(),
asset_type: '개인PC',
purchase_corp,
asset_code: 'PC-24' + String(pcIndex).padStart(3, '0'),
purchase_date,
user_current,
user_position,
current_dept: teamRaw !== '-' ? teamRaw : deptRaw,
previous_dept: pcIndex % 8 === 0 ? '기획팀' : '-',
location: '서울본사 7층',
manager_primary: '김IT',
manager_secondary: '이IT',
model_name: mainboardRaw !== '-' ? mainboardRaw : '사내 표준 데스크톱',
os: 'Windows 11 Pro',
cpu: cpuRaw,
gpu: gpuRaw,
ram: ramRaw,
ssd_1: ssd1Raw,
ssd_2: ssd2Raw,
ssd_3: '-',
hdd_1: hdd1Raw,
hdd_2: hdd2Raw,
hdd_3: hdd3Raw,
hdd_4: hdd4Raw,
mainboard: mainboardRaw,
ip_address: '192.168.0.' + (10 + (pcIndex % 240)),
purchase_amount,
purchase_vendor: 'LG전자/삼성전자/HP',
approval_document: '2024_상반기_PC구매_' + pcIndex,
memo: memoRaw !== '-' ? memoRaw : (isStock ? '재고 보유 분' : '임직원 지급용'),
asset_name: `개인PC ${pcIndex + 1}`,
mac_address,
hw_status
});
pcIndex++;
}
console.log(`Successfully parsed ${parsedPCs.length} PCs from excel file.`);
// dummyData.ts 의 나머지 데이터(dummyServers 등)를 포함하여 전체 파일을 새로 씁니다.
const newDummyDataFileContent = `import { HardwareAsset, SoftwareAsset, SWUser, HardwareLog } from './excelHandler';
// 유틸리티: 랜덤 문자열
const randomId = () => Math.random().toString(36).substring(2, 9);
// 유틸리티: 랜덤 년월 (YYYY-MM) (최근 10년)
const randomPurchaseYM = () => {
const currentYear = new Date().getFullYear();
const year = currentYear - Math.floor(Math.random() * 10);
const month = String(Math.floor(Math.random() * 12) + 1).padStart(2, '0');
return \`\${year}-\${month}\`;
};
// 유틸리티: 랜덤 YYYY-MM-DD
const randomDateStr = (maxYearsAgo = 10) => {
const currentYear = new Date().getFullYear();
const year = currentYear - Math.floor(Math.random() * maxYearsAgo);
const month = String(Math.floor(Math.random() * 12) + 1).padStart(2, '0');
const day = String(Math.floor(Math.random() * 28) + 1).padStart(2, '0');
return \`\${year}-\${month}-\${day}\`;
};
const CORPS = ['한맥', '삼안', '장헌', '장헌산업', 'PTC', '바론', '한라'];
const getRandomCorp = () => CORPS[Math.floor(Math.random() * CORPS.length)];
// ────────────────────────────────────────────────────────
// 1. SampleData_PC.xlsx 에서 파싱된 PC 데이터 주입
// ────────────────────────────────────────────────────────
export const dummyPCs: any[] = ${JSON.stringify(parsedPCs, null, 2)};
// ────────────────────────────────────────────────────────
// 2. 기타 자산 더미 데이터 (서버, 스토리지, 소프트웨어 등)
// ────────────────────────────────────────────────────────
export const dummyServers: any[] = Array.from({ length: 15 }).map((_, i) => ({
id: randomId(),
asset_type: '서버',
type2: i % 2 === 0 ? '물리' : '가상',
purchase_corp: getRandomCorp(),
asset_code: \`SRV-24\${String(i).padStart(3, '0')}\`,
purchase_date: randomPurchaseYM(),
asset_purpose: i % 2 === 0 ? '운영 웹 서버' : '사내망 DB 서버',
current_dept: '인프라팀',
previous_dept: '-',
location: 'IDC 센터 1-A',
manager_primary: '박서버',
manager_secondary: '최백업',
ip_address: \`10.0.0.\${10 + i}\`,
ip_address_2: \`192.168.100.\${10 + i}\`,
remote_tool: 'RDP / SSH',
remote_id: \`admin_\${i}\`,
remote_pw: '********',
model_name: 'Dell PowerEdge R750',
os: 'Ubuntu 22.04 LTS',
cpu: 'Intel Xeon Gold 6330',
ram: '128GB',
gpu: i % 3 === 0 ? 'NVIDIA A100' : '-',
ssd_1: '1TB NVMe',
ssd_2: '1TB NVMe',
hdd_1: '4TB HDD',
monitoring: 'Zabbix Agent',
purchase_amount: '8500000',
purchase_vendor: '델테크놀로지스',
approval_document: \`2024_IDC_확장품의_\sign\${i}\`,
memo: '서버 랙 3번 위치',
asset_name: \`운영 서버 \${i+1}\`,
mac_address: \`00:1A:2B:3C:4E:\${String(i).padStart(2, '0')}\`,
hw_status: '운영중'
}));
export const dummyStorages: any[] = Array.from({ length: 8 }).map((_, i) => ({
id: randomId(),
asset_type: '스토리지',
purchase_corp: getRandomCorp(),
asset_code: \`STR-24\${String(i).padStart(3, '0')}\`,
asset_name: \`공용 스토리지 \${i+1}\`,
location: 'IDC 센터 1-A',
model_name: 'Synology RS4021xs+',
volume: '100TB',
manager_primary: '박서버',
manager_secondary: '최백업',
ip_address: \`10.0.0.\${50 + i}\`,
mac_address: \`00:1A:2B:3C:4F:\${String(i).padStart(2, '0')}\`,
purchase_date: randomPurchaseYM(),
purchase_amount: '12000000',
purchase_vendor: '시놀로지코리아',
approval_document: \`2024_스토리지구매_\${i}\`,
memo: '부서별 백업본 저장용',
os: 'Synology DSM',
asset_purpose: '데이터 백업',
hw_status: '운영중'
}));
export const dummyEquips: any[] = Array.from({ length: 12 }).map((_, i) => ({
id: randomId(),
asset_type: '전산비품',
purchase_corp: getRandomCorp(),
asset_code: \`EQ-24\${String(i).padStart(3, '0')}\`,
asset_name: \`네트워크 스위치 \${i+1}\`,
location: '전산실 랙 1',
manager_primary: '네트워크담당자',
ip_address: \`192.168.10.\${200 + i}\`,
mac_address: \`00:1A:2B:3C:51:\${String(i).padStart(2, '0')}\`,
os: 'Cisco IOS',
purchase_date: randomPurchaseYM(),
purchase_amount: '150000',
purchase_vendor: '다나와',
approval_document: \`2024_비품구매_\${i}\`,
memo: '사내망 확장용',
asset_purpose: '네트워크 분배'
}));
export const dummyMobiles: any[] = Array.from({ length: 15 }).map((_, i) => ({
id: randomId(),
asset_type: '모바일기기',
purchase_corp: getRandomCorp(),
asset_code: \`MOB-24\${String(i).padStart(3, '0')}\`,
asset_name: \`테스트용 단말기 \${i+1}\`,
location: '개발2팀',
manager_primary: '테스터',
os: i % 2 === 0 ? 'Android 14' : 'iOS 17',
purchase_date: randomPurchaseYM(),
purchase_amount: '900000',
purchase_vendor: '삼성전자/애플',
approval_document: \`2024_모바일구매_\${i}\`,
memo: '앱 호환성 테스트 전용',
asset_purpose: 'QA 테스트',
ip_address: \`192.168.1.\${10 + i}\`,
mac_address: \`00:1A:2B:3C:50:\${String(i).padStart(2, '0')}\`
}));
export const dummySubSw: any[] = Array.from({ length: 10 }).map((_, i) => ({
id: randomId(),
sw_type: '구독SW',
sw_field: '업무용/협업',
purchase_corp: getRandomCorp(),
current_dept: '전사',
product_name: \`Microsoft 365 E\${3 + (i%2)}\`,
purchase_date: randomDateStr(3),
start_date: randomDateStr(1),
expired_date: randomDateStr(0),
purchase_amount: '150000',
asset_count: 50 + i * 5,
email_account: \`admin\${i}@hmcorp.com\`,
purchase_vendor: '소프트웨어인라이프',
memo: '연간 계약 갱신 필요'
}));
export const dummyPermSw: any[] = Array.from({ length: 5 }).map((_, i) => ({
id: randomId(),
sw_type: '영구SW',
sw_field: '디자인/설계',
purchase_corp: getRandomCorp(),
current_dept: '디자인팀',
product_name: \`AutoCAD 202\${i%4}\`,
purchase_date: randomDateStr(5),
start_date: randomDateStr(5),
expired_date: '2099-12-31',
purchase_amount: '3000000',
asset_count: 2,
email_account: \`design\${i}@hmcorp.com\`,
purchase_vendor: '오토데스크 파트너',
memo: 'USB 동글키 보관중'
}));
export const dummyCloud: any[] = Array.from({ length: 5 }).map((_, i) => ({
id: randomId(),
sw_type: '클라우드',
asset_mfr: i % 2 === 0 ? 'AWS' : 'GCP',
purchase_corp: getRandomCorp(),
current_dept: '개발팀',
product_name: \`컴퓨팅 인스턴스 Type \${i}\`,
email_account: \`awsadmin\${i}@hmcorp.com\`,
purchase_method: '법인카드(신한 1234)',
purchase_amount: \`\${500000 + i * 100000}\`,
asset_count: 1,
purchase_vendor: 'AWS/GCP',
memo: '환율 변동에 따라 매월 상이함'
}));
export const dummyDomain: any[] = Array.from({ length: 5 }).map((_, i) => ({
id: randomId(),
asset_type: '도메인',
purchase_corp: getRandomCorp(),
product_name: \`사내 운영 서비스 \${i+1}\`,
domain_address: \`service\${i+1}.hmcorp.com\`,
start_date: randomDateStr(4),
expired_date: randomDateStr(0),
purchase_amount: '22000',
manager_primary: '인프라팀장',
manager_secondary: '인프라담당자',
memo: '가비아 자동갱신 설정 완료'
}));
export const dummySwUsers: any[] = Array.from({ length: 15 }).map((_, i) => ({
id: randomId(),
sw_id: dummySubSw[0]?.id || randomId(),
purchase_corp: getRandomCorp(),
current_dept: '경영지원팀',
user_current: \`홍길동\${i}\`,
memo: \`SW신청서_2400\${i}\`
}));
export const dummyLogs: any[] = Array.from({ length: 10 }).map((_, i) => ({
id: randomId(),
assetId: dummyPCs[0]?.id || randomId(),
date: randomDateStr(1),
details: i % 2 === 0 ? '메모리 추가 증설 (16GB -> 32GB)' : '디스플레이 파손 수리',
user: 'IT지원팀',
cost: i % 2 === 0 ? 80000 : 150000,
}));
`;
fs.writeFileSync('c:/Project/HM ITAM/src/core/dummyData.ts', newDummyDataFileContent, 'utf-8');
console.log('✅ dummyData.ts file updated successfully.');
} catch (e) {
console.error('❌ Failed to update dummy data:', e);
}