feat: enhance audit logging and add company name to audit entries

- Integrated detailed audit logging for update and delete actions in the CommonCodeController and DDLController.
- Added company name retrieval to the audit log entries for better traceability.
- Updated the audit log service to include company name in the log entries.
- Modified the frontend audit log page to display company names alongside company codes for improved clarity.

Made-with: Cursor
This commit is contained in:
kjs
2026-03-12 05:14:27 +09:00
parent fd90e3d761
commit b1e50f2e0a
14 changed files with 462 additions and 142 deletions

View File

@@ -193,6 +193,7 @@ router.post(
auditLogService.log({
companyCode,
userId,
userName: req.user?.userName,
action: "CREATE",
resourceType: "NUMBERING_RULE",
resourceId: String(newRule.ruleId),
@@ -243,6 +244,7 @@ router.put(
auditLogService.log({
companyCode,
userId: req.user?.userId || "",
userName: req.user?.userName,
action: "UPDATE",
resourceType: "NUMBERING_RULE",
resourceId: ruleId,
@@ -285,6 +287,7 @@ router.delete(
auditLogService.log({
companyCode,
userId: req.user?.userId || "",
userName: req.user?.userName,
action: "DELETE",
resourceType: "NUMBERING_RULE",
resourceId: ruleId,
@@ -521,6 +524,56 @@ router.post(
companyCode,
userId
);
const isUpdate = !!ruleConfig.ruleId;
const resetPeriodLabel: Record<string, string> = {
none: "초기화 안함", daily: "일별", monthly: "월별", yearly: "연별",
};
const partTypeLabel: Record<string, string> = {
sequence: "순번", number: "숫자", date: "날짜", text: "문자", category: "카테고리", reference: "참조",
};
const partsDescription = (ruleConfig.parts || [])
.sort((a: any, b: any) => (a.order || 0) - (b.order || 0))
.map((p: any) => {
const type = partTypeLabel[p.partType] || p.partType;
if (p.partType === "text" && p.autoConfig?.textValue) return `${type}("${p.autoConfig.textValue}")`;
if (p.partType === "sequence" && p.autoConfig?.sequenceLength) return `${type}(${p.autoConfig.sequenceLength}자리)`;
if (p.partType === "date" && p.autoConfig?.dateFormat) return `${type}(${p.autoConfig.dateFormat})`;
if (p.partType === "category") return `${type}(${p.autoConfig?.categoryKey || ""})`;
if (p.partType === "reference") return `${type}(${p.autoConfig?.referenceColumnName || ""})`;
return type;
})
.join(` ${ruleConfig.separator || "-"} `);
auditLogService.log({
companyCode,
userId,
userName: req.user?.userName,
action: isUpdate ? "UPDATE" : "CREATE",
resourceType: "NUMBERING_RULE",
resourceId: String(savedRule.ruleId),
resourceName: ruleConfig.ruleName,
tableName: "numbering_rules",
summary: isUpdate
? `채번 규칙 "${ruleConfig.ruleName}" 수정`
: `채번 규칙 "${ruleConfig.ruleName}" 생성`,
changes: {
after: {
규칙명: ruleConfig.ruleName,
적용테이블: ruleConfig.tableName || "(미지정)",
적용컬럼: ruleConfig.columnName || "(미지정)",
구분자: ruleConfig.separator || "-",
리셋주기: resetPeriodLabel[ruleConfig.resetPeriod] || ruleConfig.resetPeriod || "초기화 안함",
적용범위: ruleConfig.scopeType === "menu" ? "메뉴별" : "전역",
코드구성: partsDescription || "(파트 없음)",
: (ruleConfig.parts || []).length,
},
},
ipAddress: getClientIp(req),
requestPath: req.originalUrl,
});
return res.json({ success: true, data: savedRule });
} catch (error: any) {
logger.error("[테스트] 채번 규칙 저장 실패", { error: error.message });
@@ -535,10 +588,25 @@ router.delete(
authenticateToken,
async (req: AuthenticatedRequest, res: Response) => {
const companyCode = req.user!.companyCode;
const userId = req.user!.userId;
const { ruleId } = req.params;
try {
await numberingRuleService.deleteRuleFromTest(ruleId, companyCode);
auditLogService.log({
companyCode,
userId,
userName: req.user?.userName,
action: "DELETE",
resourceType: "NUMBERING_RULE",
resourceId: ruleId,
tableName: "numbering_rules",
summary: `채번 규칙(ID:${ruleId}) 삭제`,
ipAddress: getClientIp(req),
requestPath: req.originalUrl,
});
return res.json({
success: true,
message: "테스트 채번 규칙이 삭제되었습니다",