276 lines
7.6 KiB
TypeScript
276 lines
7.6 KiB
TypeScript
import { Response } from "express";
|
|
import { AuthenticatedRequest } from "../types/auth";
|
|
import { logger } from "../utils/logger";
|
|
import { query } from "../database/db";
|
|
|
|
/**
|
|
* GET /api/system-notices
|
|
* 공지사항 목록 조회
|
|
* - 최고 관리자(*): 전체 조회
|
|
* - 일반 회사: 자신의 company_code 데이터만 조회
|
|
* - is_active 필터 옵션 지원
|
|
*/
|
|
export const getSystemNotices = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const companyCode = req.user!.companyCode;
|
|
const { is_active } = req.query;
|
|
|
|
logger.info("공지사항 목록 조회 요청", { companyCode, is_active });
|
|
|
|
const conditions: string[] = [];
|
|
const params: any[] = [];
|
|
let paramIndex = 1;
|
|
|
|
// 최고 관리자가 아닌 경우 company_code 필터링
|
|
if (companyCode !== "*") {
|
|
conditions.push(`company_code = $${paramIndex}`);
|
|
params.push(companyCode);
|
|
paramIndex++;
|
|
}
|
|
|
|
// is_active 필터 (true/false 문자열 처리)
|
|
if (is_active !== undefined && is_active !== "") {
|
|
const activeValue = is_active === "true" || is_active === "1";
|
|
conditions.push(`is_active = $${paramIndex}`);
|
|
params.push(activeValue);
|
|
paramIndex++;
|
|
}
|
|
|
|
const whereClause =
|
|
conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
|
|
const rows = await query<any>(
|
|
`SELECT
|
|
id,
|
|
company_code,
|
|
title,
|
|
content,
|
|
is_active,
|
|
created_by,
|
|
created_at,
|
|
updated_at
|
|
FROM system_notice
|
|
${whereClause}
|
|
ORDER BY created_at DESC`,
|
|
params
|
|
);
|
|
|
|
logger.info("공지사항 목록 조회 성공", {
|
|
companyCode,
|
|
count: rows.length,
|
|
});
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
data: rows,
|
|
total: rows.length,
|
|
});
|
|
} catch (error) {
|
|
logger.error("공지사항 목록 조회 실패", { error });
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "공지사항 목록 조회 중 오류가 발생했습니다.",
|
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* POST /api/system-notices
|
|
* 공지사항 등록
|
|
* - company_code는 req.user.companyCode에서 자동 추출 (클라이언트 입력 신뢰 금지)
|
|
*/
|
|
export const createSystemNotice = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const companyCode = req.user!.companyCode;
|
|
const userId = req.user!.userId;
|
|
const { title, content, is_active = true } = req.body;
|
|
|
|
logger.info("공지사항 등록 요청", { companyCode, userId, title });
|
|
|
|
if (!title || !title.trim()) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "제목을 입력해주세요.",
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!content || !content.trim()) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "내용을 입력해주세요.",
|
|
});
|
|
return;
|
|
}
|
|
|
|
const [created] = await query<any>(
|
|
`INSERT INTO system_notice (company_code, title, content, is_active, created_by)
|
|
VALUES ($1, $2, $3, $4, $5)
|
|
RETURNING *`,
|
|
[companyCode, title.trim(), content.trim(), is_active, userId]
|
|
);
|
|
|
|
logger.info("공지사항 등록 성공", {
|
|
id: created.id,
|
|
companyCode,
|
|
title: created.title,
|
|
});
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
data: created,
|
|
message: "공지사항이 등록되었습니다.",
|
|
});
|
|
} catch (error) {
|
|
logger.error("공지사항 등록 실패", { error });
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "공지사항 등록 중 오류가 발생했습니다.",
|
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* PUT /api/system-notices/:id
|
|
* 공지사항 수정
|
|
* - WHERE id=$1 AND company_code=$2 로 타 회사 데이터 수정 차단
|
|
* - 최고 관리자는 company_code 조건 없이 id만으로 수정 가능
|
|
*/
|
|
export const updateSystemNotice = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const companyCode = req.user!.companyCode;
|
|
const { id } = req.params;
|
|
const { title, content, is_active } = req.body;
|
|
|
|
logger.info("공지사항 수정 요청", { id, companyCode });
|
|
|
|
if (!title || !title.trim()) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "제목을 입력해주세요.",
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!content || !content.trim()) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "내용을 입력해주세요.",
|
|
});
|
|
return;
|
|
}
|
|
|
|
let result: any[];
|
|
|
|
if (companyCode === "*") {
|
|
// 최고 관리자: id만으로 수정
|
|
result = await query<any>(
|
|
`UPDATE system_notice
|
|
SET title = $1, content = $2, is_active = $3, updated_at = NOW()
|
|
WHERE id = $4
|
|
RETURNING *`,
|
|
[title.trim(), content.trim(), is_active ?? true, id]
|
|
);
|
|
} else {
|
|
// 일반 회사: company_code 추가 조건으로 타 회사 데이터 수정 차단
|
|
result = await query<any>(
|
|
`UPDATE system_notice
|
|
SET title = $1, content = $2, is_active = $3, updated_at = NOW()
|
|
WHERE id = $4 AND company_code = $5
|
|
RETURNING *`,
|
|
[title.trim(), content.trim(), is_active ?? true, id, companyCode]
|
|
);
|
|
}
|
|
|
|
if (!result || result.length === 0) {
|
|
res.status(404).json({
|
|
success: false,
|
|
message: "공지사항을 찾을 수 없거나 수정 권한이 없습니다.",
|
|
});
|
|
return;
|
|
}
|
|
|
|
logger.info("공지사항 수정 성공", { id, companyCode });
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
data: result[0],
|
|
message: "공지사항이 수정되었습니다.",
|
|
});
|
|
} catch (error) {
|
|
logger.error("공지사항 수정 실패", { error, id: req.params.id });
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "공지사항 수정 중 오류가 발생했습니다.",
|
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* DELETE /api/system-notices/:id
|
|
* 공지사항 삭제
|
|
* - WHERE id=$1 AND company_code=$2 로 타 회사 데이터 삭제 차단
|
|
* - 최고 관리자는 company_code 조건 없이 id만으로 삭제 가능
|
|
*/
|
|
export const deleteSystemNotice = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const companyCode = req.user!.companyCode;
|
|
const { id } = req.params;
|
|
|
|
logger.info("공지사항 삭제 요청", { id, companyCode });
|
|
|
|
let result: any[];
|
|
|
|
if (companyCode === "*") {
|
|
// 최고 관리자: id만으로 삭제
|
|
result = await query<any>(
|
|
`DELETE FROM system_notice WHERE id = $1 RETURNING id`,
|
|
[id]
|
|
);
|
|
} else {
|
|
// 일반 회사: company_code 추가 조건으로 타 회사 데이터 삭제 차단
|
|
result = await query<any>(
|
|
`DELETE FROM system_notice WHERE id = $1 AND company_code = $2 RETURNING id`,
|
|
[id, companyCode]
|
|
);
|
|
}
|
|
|
|
if (!result || result.length === 0) {
|
|
res.status(404).json({
|
|
success: false,
|
|
message: "공지사항을 찾을 수 없거나 삭제 권한이 없습니다.",
|
|
});
|
|
return;
|
|
}
|
|
|
|
logger.info("공지사항 삭제 성공", { id, companyCode });
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
message: "공지사항이 삭제되었습니다.",
|
|
});
|
|
} catch (error) {
|
|
logger.error("공지사항 삭제 실패", { error, id: req.params.id });
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "공지사항 삭제 중 오류가 발생했습니다.",
|
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
});
|
|
}
|
|
};
|