Files
vexplor_dev/backend-node/src/controllers/smartFactoryLogController.ts

219 lines
6.3 KiB
TypeScript
Raw Normal View History

// 스마트공장 활용 로그 조회 컨트롤러
// 최고관리자(*) 전용 — 회사별 필터링 가능
import { Response } from "express";
import { AuthenticatedRequest } from "../middleware/permissionMiddleware";
import { query, queryOne } from "../database/db";
import { logger } from "../utils/logger";
/**
* GET /api/admin/smart-factory-log
*
*/
export const getSmartFactoryLogs = async (
req: AuthenticatedRequest,
res: Response
): Promise<void> => {
try {
const {
companyCode,
userId,
sendStatus,
dateFrom,
dateTo,
search,
page = "1",
limit = "50",
} = req.query;
const whereConditions: string[] = [];
const queryParams: any[] = [];
let paramIndex = 1;
// 회사 필터
if (companyCode && companyCode !== "all") {
whereConditions.push(`sfl.company_code = $${paramIndex}`);
queryParams.push(companyCode);
paramIndex++;
}
// 사용자 필터
if (userId && (userId as string).trim()) {
whereConditions.push(`sfl.user_id ILIKE $${paramIndex}`);
queryParams.push(`%${(userId as string).trim()}%`);
paramIndex++;
}
// 전송 상태 필터
if (sendStatus && sendStatus !== "all") {
whereConditions.push(`sfl.send_status = $${paramIndex}`);
queryParams.push(sendStatus);
paramIndex++;
}
// 날짜 범위 필터
if (dateFrom) {
whereConditions.push(`sfl.created_at >= $${paramIndex}`);
queryParams.push(dateFrom);
paramIndex++;
}
if (dateTo) {
whereConditions.push(`sfl.created_at < ($${paramIndex}::date + 1)`);
queryParams.push(dateTo);
paramIndex++;
}
// 통합 검색
if (search && (search as string).trim()) {
whereConditions.push(
`(sfl.user_id ILIKE $${paramIndex} OR sfl.user_name ILIKE $${paramIndex} OR sfl.connect_ip ILIKE $${paramIndex} OR sfl.error_message ILIKE $${paramIndex})`
);
queryParams.push(`%${(search as string).trim()}%`);
paramIndex++;
}
const whereClause =
whereConditions.length > 0 ? `WHERE ${whereConditions.join(" AND ")}` : "";
// 총 개수
const countResult = await queryOne<{ total: string }>(
`SELECT COUNT(*) as total FROM smart_factory_log sfl ${whereClause}`,
queryParams
);
const total = parseInt(countResult?.total || "0", 10);
// 페이지네이션
const pageNum = Math.max(1, parseInt(page as string, 10));
const limitNum = Math.min(100, Math.max(1, parseInt(limit as string, 10)));
const offset = (pageNum - 1) * limitNum;
// 데이터 조회 (회사명 JOIN)
const logs = await query<any>(
`SELECT sfl.*, cm.company_name
FROM smart_factory_log sfl
LEFT JOIN company_mng cm ON cm.company_code = sfl.company_code
${whereClause}
ORDER BY sfl.created_at DESC
LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`,
[...queryParams, limitNum, offset]
);
res.status(200).json({
success: true,
data: logs,
total,
page: pageNum,
limit: limitNum,
});
} catch (error) {
logger.error("스마트공장 로그 조회 실패:", error);
res.status(500).json({
success: false,
message: "스마트공장 로그 조회 중 오류가 발생했습니다.",
error: {
code: "SERVER_ERROR",
details: error instanceof Error ? error.message : "알 수 없는 오류",
},
});
}
};
/**
* GET /api/admin/smart-factory-log/stats
* ( )
*/
export const getSmartFactoryLogStats = async (
req: AuthenticatedRequest,
res: Response
): Promise<void> => {
try {
const { companyCode, days = "30" } = req.query;
const daysNum = parseInt(days as string, 10) || 30;
const whereConditions: string[] = [
`sfl.created_at >= NOW() - INTERVAL '${daysNum} days'`,
];
const queryParams: any[] = [];
let paramIndex = 1;
if (companyCode && companyCode !== "all") {
whereConditions.push(`sfl.company_code = $${paramIndex}`);
queryParams.push(companyCode);
paramIndex++;
}
const whereClause = `WHERE ${whereConditions.join(" AND ")}`;
// 상태별 건수
const statusCounts = await query<{ send_status: string; count: string }>(
`SELECT send_status, COUNT(*) as count
FROM smart_factory_log sfl
${whereClause}
GROUP BY send_status`,
queryParams
);
// 회사별 건수
const companyCounts = await query<{
company_code: string;
company_name: string;
count: string;
}>(
`SELECT sfl.company_code, COALESCE(cm.company_name, sfl.company_code) as company_name, COUNT(*) as count
FROM smart_factory_log sfl
LEFT JOIN company_mng cm ON cm.company_code = sfl.company_code
${whereClause}
GROUP BY sfl.company_code, cm.company_name
ORDER BY count DESC`,
queryParams
);
// 일별 추이
const dailyCounts = await query<{ date: string; count: string }>(
`SELECT DATE(sfl.created_at) as date, COUNT(*) as count
FROM smart_factory_log sfl
${whereClause}
GROUP BY DATE(sfl.created_at)
ORDER BY date DESC
LIMIT ${daysNum}`,
queryParams
);
// 전체 건수
const totalResult = await queryOne<{ total: string }>(
`SELECT COUNT(*) as total FROM smart_factory_log sfl ${whereClause}`,
queryParams
);
res.status(200).json({
success: true,
data: {
total: parseInt(totalResult?.total || "0", 10),
statusCounts: statusCounts.map((r) => ({
status: r.send_status,
count: parseInt(r.count, 10),
})),
companyCounts: companyCounts.map((r) => ({
companyCode: r.company_code,
companyName: r.company_name,
count: parseInt(r.count, 10),
})),
dailyCounts: dailyCounts.map((r) => ({
date: r.date,
count: parseInt(r.count, 10),
})),
},
});
} catch (error) {
logger.error("스마트공장 로그 통계 조회 실패:", error);
res.status(500).json({
success: false,
message: "통계 조회 중 오류가 발생했습니다.",
error: {
code: "SERVER_ERROR",
details: error instanceof Error ? error.message : "알 수 없는 오류",
},
});
}
};