382 lines
10 KiB
TypeScript
382 lines
10 KiB
TypeScript
|
|
import { Response } from "express";
|
||
|
|
import { AuthenticatedRequest } from "../types/auth";
|
||
|
|
import { templateStandardService } from "../services/templateStandardService";
|
||
|
|
import { handleError } from "../utils/errorHandler";
|
||
|
|
import { checkMissingFields } from "../utils/validation";
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 템플릿 표준 관리 컨트롤러
|
||
|
|
*/
|
||
|
|
export class TemplateStandardController {
|
||
|
|
/**
|
||
|
|
* 템플릿 목록 조회
|
||
|
|
*/
|
||
|
|
async getTemplates(req: AuthenticatedRequest, res: Response) {
|
||
|
|
try {
|
||
|
|
const {
|
||
|
|
active = "Y",
|
||
|
|
category,
|
||
|
|
search,
|
||
|
|
companyCode,
|
||
|
|
is_public = "Y",
|
||
|
|
page = "1",
|
||
|
|
limit = "50",
|
||
|
|
} = req.query;
|
||
|
|
|
||
|
|
const user = req.user;
|
||
|
|
const userCompanyCode = user?.companyCode || "DEFAULT";
|
||
|
|
|
||
|
|
const result = await templateStandardService.getTemplates({
|
||
|
|
active: active as string,
|
||
|
|
category: category as string,
|
||
|
|
search: search as string,
|
||
|
|
company_code: (companyCode as string) || userCompanyCode,
|
||
|
|
is_public: is_public as string,
|
||
|
|
page: parseInt(page as string),
|
||
|
|
limit: parseInt(limit as string),
|
||
|
|
});
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
success: true,
|
||
|
|
data: result.templates,
|
||
|
|
pagination: {
|
||
|
|
total: result.total,
|
||
|
|
page: parseInt(page as string),
|
||
|
|
limit: parseInt(limit as string),
|
||
|
|
totalPages: Math.ceil(result.total / parseInt(limit as string)),
|
||
|
|
},
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
return handleError(
|
||
|
|
res,
|
||
|
|
error,
|
||
|
|
"템플릿 목록 조회 중 오류가 발생했습니다."
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 템플릿 상세 조회
|
||
|
|
*/
|
||
|
|
async getTemplate(req: AuthenticatedRequest, res: Response) {
|
||
|
|
try {
|
||
|
|
const { templateCode } = req.params;
|
||
|
|
|
||
|
|
if (!templateCode) {
|
||
|
|
return res.status(400).json({
|
||
|
|
success: false,
|
||
|
|
error: "템플릿 코드가 필요합니다.",
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
const template = await templateStandardService.getTemplate(templateCode);
|
||
|
|
|
||
|
|
if (!template) {
|
||
|
|
return res.status(404).json({
|
||
|
|
success: false,
|
||
|
|
error: "템플릿을 찾을 수 없습니다.",
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
success: true,
|
||
|
|
data: template,
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
return handleError(res, error, "템플릿 조회 중 오류가 발생했습니다.");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 템플릿 생성
|
||
|
|
*/
|
||
|
|
async createTemplate(req: AuthenticatedRequest, res: Response) {
|
||
|
|
try {
|
||
|
|
const user = req.user;
|
||
|
|
const templateData = req.body;
|
||
|
|
|
||
|
|
// 필수 필드 검증
|
||
|
|
const requiredFields = [
|
||
|
|
"template_code",
|
||
|
|
"template_name",
|
||
|
|
"category",
|
||
|
|
"layout_config",
|
||
|
|
];
|
||
|
|
const missingFields = checkMissingFields(templateData, requiredFields);
|
||
|
|
|
||
|
|
if (missingFields.length > 0) {
|
||
|
|
return res.status(400).json({
|
||
|
|
success: false,
|
||
|
|
error: `필수 필드가 누락되었습니다: ${missingFields.join(", ")}`,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 회사 코드와 생성자 정보 추가
|
||
|
|
const templateWithMeta = {
|
||
|
|
...templateData,
|
||
|
|
company_code: user?.companyCode || "DEFAULT",
|
||
|
|
created_by: user?.userId || "system",
|
||
|
|
updated_by: user?.userId || "system",
|
||
|
|
};
|
||
|
|
|
||
|
|
const newTemplate =
|
||
|
|
await templateStandardService.createTemplate(templateWithMeta);
|
||
|
|
|
||
|
|
res.status(201).json({
|
||
|
|
success: true,
|
||
|
|
data: newTemplate,
|
||
|
|
message: "템플릿이 성공적으로 생성되었습니다.",
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
return handleError(res, error, "템플릿 생성 중 오류가 발생했습니다.");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 템플릿 수정
|
||
|
|
*/
|
||
|
|
async updateTemplate(req: AuthenticatedRequest, res: Response) {
|
||
|
|
try {
|
||
|
|
const { templateCode } = req.params;
|
||
|
|
const templateData = req.body;
|
||
|
|
const user = req.user;
|
||
|
|
|
||
|
|
if (!templateCode) {
|
||
|
|
return res.status(400).json({
|
||
|
|
success: false,
|
||
|
|
error: "템플릿 코드가 필요합니다.",
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 수정자 정보 추가
|
||
|
|
const templateWithMeta = {
|
||
|
|
...templateData,
|
||
|
|
updated_by: user?.userId || "system",
|
||
|
|
};
|
||
|
|
|
||
|
|
const updatedTemplate = await templateStandardService.updateTemplate(
|
||
|
|
templateCode,
|
||
|
|
templateWithMeta
|
||
|
|
);
|
||
|
|
|
||
|
|
if (!updatedTemplate) {
|
||
|
|
return res.status(404).json({
|
||
|
|
success: false,
|
||
|
|
error: "템플릿을 찾을 수 없습니다.",
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
success: true,
|
||
|
|
data: updatedTemplate,
|
||
|
|
message: "템플릿이 성공적으로 수정되었습니다.",
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
return handleError(res, error, "템플릿 수정 중 오류가 발생했습니다.");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 템플릿 삭제
|
||
|
|
*/
|
||
|
|
async deleteTemplate(req: AuthenticatedRequest, res: Response) {
|
||
|
|
try {
|
||
|
|
const { templateCode } = req.params;
|
||
|
|
|
||
|
|
if (!templateCode) {
|
||
|
|
return res.status(400).json({
|
||
|
|
success: false,
|
||
|
|
error: "템플릿 코드가 필요합니다.",
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
const deleted =
|
||
|
|
await templateStandardService.deleteTemplate(templateCode);
|
||
|
|
|
||
|
|
if (!deleted) {
|
||
|
|
return res.status(404).json({
|
||
|
|
success: false,
|
||
|
|
error: "템플릿을 찾을 수 없습니다.",
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
success: true,
|
||
|
|
message: "템플릿이 성공적으로 삭제되었습니다.",
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
return handleError(res, error, "템플릿 삭제 중 오류가 발생했습니다.");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 템플릿 정렬 순서 일괄 업데이트
|
||
|
|
*/
|
||
|
|
async updateSortOrder(req: AuthenticatedRequest, res: Response) {
|
||
|
|
try {
|
||
|
|
const { templates } = req.body;
|
||
|
|
|
||
|
|
if (!Array.isArray(templates)) {
|
||
|
|
return res.status(400).json({
|
||
|
|
success: false,
|
||
|
|
error: "templates는 배열이어야 합니다.",
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
await templateStandardService.updateSortOrder(templates);
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
success: true,
|
||
|
|
message: "템플릿 정렬 순서가 성공적으로 업데이트되었습니다.",
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
return handleError(
|
||
|
|
res,
|
||
|
|
error,
|
||
|
|
"템플릿 정렬 순서 업데이트 중 오류가 발생했습니다."
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 템플릿 복제
|
||
|
|
*/
|
||
|
|
async duplicateTemplate(req: AuthenticatedRequest, res: Response) {
|
||
|
|
try {
|
||
|
|
const { templateCode } = req.params;
|
||
|
|
const { new_template_code, new_template_name } = req.body;
|
||
|
|
const user = req.user;
|
||
|
|
|
||
|
|
if (!templateCode || !new_template_code || !new_template_name) {
|
||
|
|
return res.status(400).json({
|
||
|
|
success: false,
|
||
|
|
error: "필수 필드가 누락되었습니다.",
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
const duplicatedTemplate =
|
||
|
|
await templateStandardService.duplicateTemplate({
|
||
|
|
originalCode: templateCode,
|
||
|
|
newCode: new_template_code,
|
||
|
|
newName: new_template_name,
|
||
|
|
company_code: user?.companyCode || "DEFAULT",
|
||
|
|
created_by: user?.userId || "system",
|
||
|
|
});
|
||
|
|
|
||
|
|
res.status(201).json({
|
||
|
|
success: true,
|
||
|
|
data: duplicatedTemplate,
|
||
|
|
message: "템플릿이 성공적으로 복제되었습니다.",
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
return handleError(res, error, "템플릿 복제 중 오류가 발생했습니다.");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 템플릿 카테고리 목록 조회
|
||
|
|
*/
|
||
|
|
async getCategories(req: AuthenticatedRequest, res: Response) {
|
||
|
|
try {
|
||
|
|
const user = req.user;
|
||
|
|
const companyCode = user?.companyCode || "DEFAULT";
|
||
|
|
|
||
|
|
const categories =
|
||
|
|
await templateStandardService.getCategories(companyCode);
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
success: true,
|
||
|
|
data: categories,
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
return handleError(
|
||
|
|
res,
|
||
|
|
error,
|
||
|
|
"템플릿 카테고리 조회 중 오류가 발생했습니다."
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 템플릿 가져오기 (JSON 파일에서)
|
||
|
|
*/
|
||
|
|
async importTemplate(req: AuthenticatedRequest, res: Response) {
|
||
|
|
try {
|
||
|
|
const user = req.user;
|
||
|
|
const templateData = req.body;
|
||
|
|
|
||
|
|
if (!templateData.layout_config) {
|
||
|
|
return res.status(400).json({
|
||
|
|
success: false,
|
||
|
|
error: "유효한 템플릿 데이터가 아닙니다.",
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 회사 코드와 생성자 정보 추가
|
||
|
|
const templateWithMeta = {
|
||
|
|
...templateData,
|
||
|
|
company_code: user?.companyCode || "DEFAULT",
|
||
|
|
created_by: user?.userId || "system",
|
||
|
|
updated_by: user?.userId || "system",
|
||
|
|
};
|
||
|
|
|
||
|
|
const importedTemplate =
|
||
|
|
await templateStandardService.createTemplate(templateWithMeta);
|
||
|
|
|
||
|
|
res.status(201).json({
|
||
|
|
success: true,
|
||
|
|
data: importedTemplate,
|
||
|
|
message: "템플릿이 성공적으로 가져왔습니다.",
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
return handleError(res, error, "템플릿 가져오기 중 오류가 발생했습니다.");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 템플릿 내보내기 (JSON 형태로)
|
||
|
|
*/
|
||
|
|
async exportTemplate(req: AuthenticatedRequest, res: Response) {
|
||
|
|
try {
|
||
|
|
const { templateCode } = req.params;
|
||
|
|
|
||
|
|
if (!templateCode) {
|
||
|
|
return res.status(400).json({
|
||
|
|
success: false,
|
||
|
|
error: "템플릿 코드가 필요합니다.",
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
const template = await templateStandardService.getTemplate(templateCode);
|
||
|
|
|
||
|
|
if (!template) {
|
||
|
|
return res.status(404).json({
|
||
|
|
success: false,
|
||
|
|
error: "템플릿을 찾을 수 없습니다.",
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 내보내기용 데이터 (메타데이터 제외)
|
||
|
|
const exportData = {
|
||
|
|
template_code: template.template_code,
|
||
|
|
template_name: template.template_name,
|
||
|
|
template_name_eng: template.template_name_eng,
|
||
|
|
description: template.description,
|
||
|
|
category: template.category,
|
||
|
|
icon_name: template.icon_name,
|
||
|
|
default_size: template.default_size,
|
||
|
|
layout_config: template.layout_config,
|
||
|
|
};
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
success: true,
|
||
|
|
data: exportData,
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
return handleError(res, error, "템플릿 내보내기 중 오류가 발생했습니다.");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export const templateStandardController = new TemplateStandardController();
|