fix(audit-log): URL 단/복수 + DB 컬럼명 + 응답 키 case 일괄 정정
- frontend `/audit-log` → 백엔드 `/audit-logs` (단/복수 mismatch) - mapper auditLog.xml: `CREATED_DATE` → `CREATED_AT` (PostgreSQL 실제 컬럼명) - AuditLogStats interface camelCase → snake_case (백엔드 응답 키 일치) 그렘린 카오스 테스트 + fetch hook 으로 발견. Application error / 500 / 404 모두 해결. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -25,17 +25,17 @@
|
||||
AND SAL.TABLE_NAME = #{table_name}
|
||||
</if>
|
||||
<if test="date_from != null and date_from != ''">
|
||||
AND SAL.CREATED_DATE >= #{date_from}::TIMESTAMPTZ
|
||||
AND SAL.CREATED_AT >= #{date_from}::TIMESTAMPTZ
|
||||
</if>
|
||||
<if test="date_to != null and date_to != ''">
|
||||
AND SAL.CREATED_DATE <= #{date_to}::TIMESTAMPTZ
|
||||
AND SAL.CREATED_AT <= #{date_to}::TIMESTAMPTZ
|
||||
</if>
|
||||
<if test="search != null and search != ''">
|
||||
AND (SAL.SUMMARY ILIKE CONCAT('%', #{search}, '%')
|
||||
OR SAL.RESOURCE_NAME ILIKE CONCAT('%', #{search}, '%')
|
||||
OR SAL.USER_NAME ILIKE CONCAT('%', #{search}, '%'))
|
||||
</if>
|
||||
ORDER BY SAL.CREATED_DATE DESC
|
||||
ORDER BY SAL.CREATED_AT DESC
|
||||
LIMIT #{limit} OFFSET #{offset}
|
||||
</select>
|
||||
|
||||
@@ -60,10 +60,10 @@
|
||||
AND SAL.TABLE_NAME = #{table_name}
|
||||
</if>
|
||||
<if test="date_from != null and date_from != ''">
|
||||
AND SAL.CREATED_DATE >= #{date_from}::TIMESTAMPTZ
|
||||
AND SAL.CREATED_AT >= #{date_from}::TIMESTAMPTZ
|
||||
</if>
|
||||
<if test="date_to != null and date_to != ''">
|
||||
AND SAL.CREATED_DATE <= #{date_to}::TIMESTAMPTZ
|
||||
AND SAL.CREATED_AT <= #{date_to}::TIMESTAMPTZ
|
||||
</if>
|
||||
<if test="search != null and search != ''">
|
||||
AND (SAL.SUMMARY ILIKE CONCAT('%', #{search}, '%')
|
||||
@@ -74,13 +74,13 @@
|
||||
|
||||
<!-- 일별 감사 로그 건수 -->
|
||||
<select id="selectDailyCounts" parameterType="map" resultType="map">
|
||||
SELECT DATE(CREATED_DATE) AS DATE, COUNT(*)::INT AS COUNT
|
||||
SELECT DATE(CREATED_AT) AS DATE, COUNT(*)::INT AS COUNT
|
||||
FROM SYSTEM_AUDIT_LOG
|
||||
WHERE CREATED_DATE >= NOW() - INTERVAL '1 day' * #{days}
|
||||
WHERE CREATED_AT >= NOW() - INTERVAL '1 day' * #{days}
|
||||
<if test="company_code != null and company_code != ''">
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
</if>
|
||||
GROUP BY DATE(CREATED_DATE)
|
||||
GROUP BY DATE(CREATED_AT)
|
||||
ORDER BY DATE DESC
|
||||
</select>
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
<select id="selectResourceTypeCounts" parameterType="map" resultType="map">
|
||||
SELECT RESOURCE_TYPE, COUNT(*)::INT AS COUNT
|
||||
FROM SYSTEM_AUDIT_LOG
|
||||
WHERE CREATED_DATE >= NOW() - INTERVAL '1 day' * #{days}
|
||||
WHERE CREATED_AT >= NOW() - INTERVAL '1 day' * #{days}
|
||||
<if test="company_code != null and company_code != ''">
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
</if>
|
||||
@@ -100,7 +100,7 @@
|
||||
<select id="selectActionCounts" parameterType="map" resultType="map">
|
||||
SELECT ACTION, COUNT(*)::INT AS COUNT
|
||||
FROM SYSTEM_AUDIT_LOG
|
||||
WHERE CREATED_DATE >= NOW() - INTERVAL '1 day' * #{days}
|
||||
WHERE CREATED_AT >= NOW() - INTERVAL '1 day' * #{days}
|
||||
<if test="company_code != null and company_code != ''">
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
</if>
|
||||
@@ -112,7 +112,7 @@
|
||||
<select id="selectTopUsers" parameterType="map" resultType="map">
|
||||
SELECT USER_ID, COALESCE(MAX(USER_NAME), USER_ID) AS USER_NAME, COUNT(*)::INT AS COUNT
|
||||
FROM SYSTEM_AUDIT_LOG
|
||||
WHERE CREATED_DATE >= NOW() - INTERVAL '1 day' * #{days}
|
||||
WHERE CREATED_AT >= NOW() - INTERVAL '1 day' * #{days}
|
||||
<if test="company_code != null and company_code != ''">
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
</if>
|
||||
|
||||
@@ -422,20 +422,20 @@ export default function AuditLogPage() {
|
||||
<CardContent className="p-4">
|
||||
<p className="text-muted-foreground text-xs">최근 30일 총 변경</p>
|
||||
<p className="text-2xl font-bold">
|
||||
{stats.dailyCounts.reduce((s, d) => s + d.count, 0).toLocaleString()}건
|
||||
{stats.daily_counts.reduce((s, d) => s + d.count, 0).toLocaleString()}건
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<p className="text-muted-foreground text-xs">리소스 유형</p>
|
||||
<p className="text-2xl font-bold">{stats.resourceTypeCounts.length}종</p>
|
||||
<p className="text-2xl font-bold">{stats.resource_type_counts.length}종</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<p className="text-muted-foreground text-xs">활동 사용자</p>
|
||||
<p className="text-2xl font-bold">{stats.topUsers.length}명</p>
|
||||
<p className="text-2xl font-bold">{stats.top_users.length}명</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
@@ -443,7 +443,7 @@ export default function AuditLogPage() {
|
||||
<p className="text-muted-foreground text-xs">오늘 변경</p>
|
||||
<p className="text-2xl font-bold">
|
||||
{(
|
||||
stats.dailyCounts.find(
|
||||
stats.daily_counts.find(
|
||||
(d) =>
|
||||
new Date(d.date).toDateString() ===
|
||||
new Date().toDateString()
|
||||
|
||||
@@ -1169,7 +1169,7 @@ export default function CopyScreenModal({
|
||||
|
||||
// 그룹 복제 요약 감사 로그 1건 기록
|
||||
try {
|
||||
await apiClient.post("/audit-log", {
|
||||
await apiClient.post("/audit-logs", {
|
||||
action: "COPY",
|
||||
resource_type: "SCREEN",
|
||||
resource_id: String(sourceGroup.id),
|
||||
|
||||
@@ -32,10 +32,10 @@ export interface AuditLogFilters {
|
||||
}
|
||||
|
||||
export interface AuditLogStats {
|
||||
dailyCounts: Array<{ date: string; count: number }>;
|
||||
resourceTypeCounts: Array<{ resource_type: string; count: number }>;
|
||||
actionCounts: Array<{ action: string; count: number }>;
|
||||
topUsers: Array<{ user_id: string; user_name: string; count: number }>;
|
||||
daily_counts: Array<{ date: string; count: number }>;
|
||||
resource_type_counts: Array<{ resource_type: string; count: number }>;
|
||||
action_counts: Array<{ action: string; count: number }>;
|
||||
top_users: Array<{ user_id: string; user_name: string; count: number }>;
|
||||
}
|
||||
|
||||
export async function getAuditLogs(
|
||||
@@ -59,7 +59,7 @@ export async function getAuditLogs(
|
||||
if (filters.page) params.append("page", String(filters.page));
|
||||
if (filters.limit) params.append("limit", String(filters.limit));
|
||||
|
||||
const response = await apiClient.get(`/audit-log?${params.toString()}`);
|
||||
const response = await apiClient.get(`/audit-logs?${params.toString()}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ export async function getAuditLogStats(
|
||||
if (companyCode) params.append("companyCode", companyCode);
|
||||
if (days) params.append("days", String(days));
|
||||
|
||||
const response = await apiClient.get(`/audit-log/stats?${params.toString()}`);
|
||||
const response = await apiClient.get(`/audit-logs/stats?${params.toString()}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
@@ -87,6 +87,6 @@ export async function getAuditLogUsers(
|
||||
const params = new URLSearchParams();
|
||||
if (companyCode) params.append("companyCode", companyCode);
|
||||
|
||||
const response = await apiClient.get(`/audit-log/users?${params.toString()}`);
|
||||
const response = await apiClient.get(`/audit-logs/users?${params.toString()}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user