165 lines
5.6 KiB
JavaScript
165 lines
5.6 KiB
JavaScript
const XLSX = require('xlsx');
|
|
const mysql = require('mysql2/promise');
|
|
const dotenv = require('dotenv');
|
|
const path = require('path');
|
|
|
|
dotenv.config({ path: path.join(__dirname, '../.env') });
|
|
|
|
const { DB_HOST, DB_USER, DB_PASS, DB_NAME, DB_PORT } = process.env;
|
|
|
|
// 용량 정제 함수
|
|
function parseCapacity(val) {
|
|
if (!val || val === '0' || val === 0) return null;
|
|
|
|
let str = String(val).toUpperCase();
|
|
|
|
// 1. 괄호와 그 안의 내용 제거
|
|
str = str.replace(/\(.*\)/g, '').trim();
|
|
|
|
// 2. 숫자와 단위 분리
|
|
const numMatch = str.match(/[\d.]+/);
|
|
if (!numMatch) return null;
|
|
|
|
let num = parseFloat(numMatch[0]);
|
|
let unit = 'GB'; // 기본 단위
|
|
|
|
if (str.includes('TB')) {
|
|
unit = 'TB';
|
|
} else if (str.includes('GB')) {
|
|
// 4자리수 GB인 경우 TB로 전환 (지시사항 1번)
|
|
if (num >= 1000) {
|
|
num = num / 1000;
|
|
unit = 'TB';
|
|
} else {
|
|
unit = 'GB';
|
|
}
|
|
} else {
|
|
// 단위가 명시되지 않은 경우 숫자의 크기로 판단
|
|
if (num >= 1000) {
|
|
num = num / 1000;
|
|
unit = 'TB';
|
|
}
|
|
}
|
|
|
|
return {
|
|
capacity: parseFloat(num.toFixed(2)),
|
|
unit: unit
|
|
};
|
|
}
|
|
|
|
async function importAssets() {
|
|
const connection = await mysql.createConnection({
|
|
host: DB_HOST,
|
|
user: DB_USER,
|
|
password: DB_PASS,
|
|
database: DB_NAME,
|
|
port: parseInt(DB_PORT || '3306')
|
|
});
|
|
|
|
console.log('🚀 [Step 1] 데이터 로드 및 사전 준비 (정제 로직 강화)...');
|
|
|
|
const workbook = XLSX.readFile('asset_pc (2026.06.15).xlsx');
|
|
const sheet = workbook.Sheets[workbook.SheetNames[0]];
|
|
const rawData = XLSX.utils.sheet_to_json(sheet);
|
|
|
|
// system_users 데이터 맵
|
|
const [userRows] = await connection.query('SELECT emp_no, user_name, dept_name, position, status FROM system_users');
|
|
const userMap = new Map();
|
|
userRows.forEach(u => userMap.set(String(u.emp_no), u));
|
|
|
|
// 기존 자산 중복 체크용 (emp_no + asset_type + category + user_current)
|
|
const [existingAssets] = await connection.query('SELECT emp_no, asset_type, category, user_current FROM asset_core');
|
|
const existingSet = new Set();
|
|
existingAssets.forEach(a => {
|
|
existingSet.add(`${a.emp_no || ''}|${a.asset_type}|${a.category}|${a.user_current}`);
|
|
});
|
|
|
|
console.log(`📊 처리 대상 데이터: ${rawData.length}건`);
|
|
|
|
let skipCount = 0;
|
|
let insertCount = 0;
|
|
let errorCount = 0;
|
|
|
|
for (let i = 0; i < rawData.length; i++) {
|
|
const row = rawData[i];
|
|
const empNo = row.emp_no ? String(row.emp_no) : ''; // 사번 없는 행 처리 (지시사항 3번)
|
|
const assetType = row.asset_type || '개인PC';
|
|
const category = row.category || 'PC';
|
|
const userCurrent = row.user_current || '';
|
|
|
|
// 중복 체크
|
|
const dupKey = `${empNo}|${assetType}|${category}|${userCurrent}`;
|
|
if (existingSet.has(dupKey)) {
|
|
skipCount++;
|
|
continue;
|
|
}
|
|
|
|
// [Step 2] 데이터 정제
|
|
const matchedUser = empNo ? userMap.get(empNo) : null;
|
|
const userName = matchedUser ? matchedUser.user_name : userCurrent;
|
|
const deptName = matchedUser ? matchedUser.dept_name : (row.current_dept || '');
|
|
const position = matchedUser ? matchedUser.position : '';
|
|
|
|
const d1 = parseInt(row.purchase_date_1) || 0;
|
|
const d2 = parseInt(row.purchase_date_2) || 0;
|
|
const purchaseDate = Math.max(d1, d2) > 0 ? String(Math.max(d1, d2)) : '';
|
|
|
|
const assetId = `PC_20260615_${String(i + 1).padStart(4, '0')}`;
|
|
const now = new Date().toISOString().replace('T', ' ').substring(0, 19);
|
|
|
|
try {
|
|
// [Step 3] DB 입력
|
|
// A. asset_core
|
|
await connection.query(
|
|
`INSERT INTO asset_core (id, asset_code, category, asset_type, current_role, asset_purpose, service_type,
|
|
purchase_date, memo, current_dept, user_current, emp_no, user_position, created_at, updated_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
[assetId, assetId, category, assetType, row.current_role || '', row.asset_purpose || '', row.service_type || '',
|
|
purchaseDate, row.memo || '', deptName, userName, empNo, position, now, now]
|
|
);
|
|
|
|
// B. asset_spec
|
|
await connection.query(
|
|
`INSERT INTO asset_spec (asset_id, mainboard, cpu, ram, gpu) VALUES (?, ?, ?, ?, ?)`,
|
|
[assetId, row.mainboard || '', row.cpu || '', row.ram || '', row.gpu || '']
|
|
);
|
|
|
|
// C. asset_volume
|
|
const volCols = [
|
|
{ key: 'SDD1', type: 'SSD', slot: 1 },
|
|
{ key: 'SDD2', type: 'SSD', slot: 2 },
|
|
{ key: 'HDD1', type: 'HDD', slot: 3 },
|
|
{ key: 'HDD2', type: 'HDD', slot: 4 },
|
|
{ key: 'HDD3', type: 'HDD', slot: 5 },
|
|
{ key: 'HDD4', type: 'HDD', slot: 6 }
|
|
];
|
|
|
|
for (const col of volCols) {
|
|
const rawVol = row[col.key];
|
|
const parsed = parseCapacity(rawVol);
|
|
if (parsed) {
|
|
await connection.query(
|
|
`INSERT INTO asset_volume (asset_id, disk_type, capacity, unit, slot_no) VALUES (?, ?, ?, ?, ?)`,
|
|
[assetId, col.type, parsed.capacity, parsed.unit, col.slot]
|
|
);
|
|
}
|
|
}
|
|
|
|
insertCount++;
|
|
existingSet.add(dupKey);
|
|
} catch (err) {
|
|
errorCount++;
|
|
console.error(`❌ [Row ${i + 2}] ${empNo || 'Public'}: ${err.message}`);
|
|
}
|
|
}
|
|
|
|
console.log(`\n✨ 작업 완료!`);
|
|
console.log(`- 신규 입력: ${insertCount}건`);
|
|
console.log(`- 중복 스킵: ${skipCount}건`);
|
|
console.log(`- 오류 실패: ${errorCount}건`);
|
|
|
|
await connection.end();
|
|
}
|
|
|
|
importAssets().catch(console.error);
|