feat: 자산 정보 수정 시 메모, 용도, 접속정보, 유형 변경 사항도 시스템 이력에 기록되도록 개선

This commit is contained in:
이태훈
2026-06-25 13:54:49 +09:00
parent 6ed939c6bf
commit cbfc1bcd1d

View File

@@ -288,6 +288,7 @@ app.post('/api/asset/:category/save', async (req, res) => {
// 3.0 History Tracking & Auto Field Update
const [oldCoreRows] = await connection.query('SELECT * FROM asset_core WHERE id = ?', [asset.id]);
const [oldSpecRows] = await connection.query('SELECT * FROM asset_spec WHERE asset_id = ?', [asset.id]);
const [oldRemoteRows] = await connection.query('SELECT * FROM asset_remote WHERE asset_id = ? AND is_active = 1', [asset.id]);
const oldCore = oldCoreRows[0] || {};
const oldSpec = oldSpecRows[0] || {};
@@ -350,6 +351,85 @@ app.post('/api/asset/:category/save', async (req, res) => {
});
}
// 3.0.4 메모, 용도, 유형 변동 감지
const oldMemo = String(oldCore.memo || '').trim();
const newMemo = String(asset.memo || '').trim();
if (newMemo !== '' && oldMemo !== newMemo) {
historyLogs.push({
event_type: 'MEMO_CHANGE',
details: `[메모 변경] ${oldMemo || '(없음)'} -> ${newMemo}`
});
}
const oldPurpose = String(oldCore.asset_purpose || '').trim();
const newPurpose = String(asset.asset_purpose || '').trim();
if (newPurpose !== '' && oldPurpose !== newPurpose) {
historyLogs.push({
event_type: 'PURPOSE_CHANGE',
details: `[용도 변경] ${oldPurpose || '(없음)'} -> ${newPurpose}`
});
}
const oldType = String(oldCore.asset_type || '').trim();
const newType = String(asset.asset_type || '').trim();
if (newType !== '' && oldType !== newType) {
historyLogs.push({
event_type: 'TYPE_CHANGE',
details: `[유형 변경] ${oldType || '(없음)'} -> ${newType}`
});
}
// 3.0.5 접속정보 변동 감지
const formatRemote = (r) => {
const type = r.net_type || r.type || '';
const name = r.net_name || r.name || '';
const val1 = r.net_value1 || r.val1 || '';
const val2 = r.net_value2 || r.val2 || '';
return `${type}:${name}:${val1}:${val2}`;
};
const oldRemotesSummary = oldRemoteRows.map(formatRemote).sort().join(' | ');
let newNets = [];
if (asset.remotes) {
try {
newNets = typeof asset.remotes === 'string' ? JSON.parse(asset.remotes) : asset.remotes;
} catch(e) { newNets = []; }
} else if (asset.ip_address || asset.mac_address || asset.remote_tool) {
if (asset.ip_address || asset.mac_address) {
newNets.push({ type: 'IP', name: '기본망', val1: asset.ip_address, val2: asset.mac_address });
}
if (asset.remote_tool || asset.remote_id || asset.remote_pw) {
newNets.push({ type: 'REMOTE', name: asset.remote_tool, val1: asset.remote_id, val2: asset.remote_pw });
}
}
const newRemotesSummary = newNets.map(formatRemote).sort().join(' | ');
if (newRemotesSummary !== '' && oldRemotesSummary !== newRemotesSummary) {
const formatDisplay = (summary) => {
if (!summary) return '(없음)';
return summary.split(' | ').map(item => {
const [type, name, val1, val2] = item.split(':');
if (type === 'IP') {
return `[IP] ${name}: ${val1} (MAC: ${val2 || '없음'})`;
} else {
let id = '', pw = '';
try {
const parsed = JSON.parse(val2);
id = parsed.id || '';
pw = parsed.pw || '';
} catch(e) { id = val1; pw = val2; }
return `[원격] ${name}: ID=${id || '없음'}, PW=${pw ? '***' : '없음'}`;
}
}).join(', ');
};
historyLogs.push({
event_type: 'REMOTE_CHANGE',
details: `[접속정보 변경] ${formatDisplay(oldRemotesSummary)} -> ${formatDisplay(newRemotesSummary)}`
});
}
// 로그 일괄 삽입
for (const log of historyLogs) {
await connection.query(