리포트 템플릿 저장 구현
This commit is contained in:
@@ -302,6 +302,101 @@ export class ReportController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 현재 리포트를 템플릿으로 저장
|
||||
* POST /api/admin/reports/:reportId/save-as-template
|
||||
*/
|
||||
async saveAsTemplate(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const { reportId } = req.params;
|
||||
const { templateNameKor, templateNameEng, description } = req.body;
|
||||
const userId = (req as any).user?.userId || "SYSTEM";
|
||||
|
||||
// 필수 필드 검증
|
||||
if (!templateNameKor) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "템플릿명은 필수입니다.",
|
||||
});
|
||||
}
|
||||
|
||||
const templateId = await reportService.saveAsTemplate(
|
||||
reportId,
|
||||
templateNameKor,
|
||||
templateNameEng,
|
||||
description,
|
||||
userId
|
||||
);
|
||||
|
||||
return res.status(201).json({
|
||||
success: true,
|
||||
data: {
|
||||
templateId,
|
||||
},
|
||||
message: "템플릿이 저장되었습니다.",
|
||||
});
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 레이아웃 데이터로 직접 템플릿 생성 (리포트 저장 불필요)
|
||||
* POST /api/admin/reports/templates/create-from-layout
|
||||
*/
|
||||
async createTemplateFromLayout(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
try {
|
||||
const {
|
||||
templateNameKor,
|
||||
templateNameEng,
|
||||
templateType,
|
||||
description,
|
||||
layoutConfig,
|
||||
defaultQueries = [],
|
||||
} = req.body;
|
||||
const userId = (req as any).user?.userId || "SYSTEM";
|
||||
|
||||
// 필수 필드 검증
|
||||
if (!templateNameKor) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "템플릿명은 필수입니다.",
|
||||
});
|
||||
}
|
||||
|
||||
if (!layoutConfig) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "레이아웃 설정은 필수입니다.",
|
||||
});
|
||||
}
|
||||
|
||||
const templateId = await reportService.createTemplateFromLayout(
|
||||
templateNameKor,
|
||||
templateNameEng,
|
||||
templateType || "GENERAL",
|
||||
description,
|
||||
layoutConfig,
|
||||
defaultQueries,
|
||||
userId
|
||||
);
|
||||
|
||||
return res.status(201).json({
|
||||
success: true,
|
||||
data: {
|
||||
templateId,
|
||||
},
|
||||
message: "템플릿이 생성되었습니다.",
|
||||
});
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 템플릿 삭제
|
||||
* DELETE /api/admin/reports/templates/:templateId
|
||||
|
||||
@@ -19,6 +19,10 @@ router.get("/templates", (req, res, next) =>
|
||||
router.post("/templates", (req, res, next) =>
|
||||
reportController.createTemplate(req, res, next)
|
||||
);
|
||||
// 레이아웃 데이터로 직접 템플릿 생성 (리포트 저장 불필요)
|
||||
router.post("/templates/create-from-layout", (req, res, next) =>
|
||||
reportController.createTemplateFromLayout(req, res, next)
|
||||
);
|
||||
router.delete("/templates/:templateId", (req, res, next) =>
|
||||
reportController.deleteTemplate(req, res, next)
|
||||
);
|
||||
@@ -38,6 +42,11 @@ router.post("/:reportId/copy", (req, res, next) =>
|
||||
reportController.copyReport(req, res, next)
|
||||
);
|
||||
|
||||
// 템플릿으로 저장
|
||||
router.post("/:reportId/save-as-template", (req, res, next) =>
|
||||
reportController.saveAsTemplate(req, res, next)
|
||||
);
|
||||
|
||||
// 레이아웃 관련 라우트
|
||||
router.get("/:reportId/layout", (req, res, next) =>
|
||||
reportController.getLayout(req, res, next)
|
||||
|
||||
@@ -821,6 +821,185 @@ export class ReportService {
|
||||
const result = await query(deleteQuery, [templateId]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 현재 리포트를 템플릿으로 저장
|
||||
*/
|
||||
async saveAsTemplate(
|
||||
reportId: string,
|
||||
templateNameKor: string,
|
||||
templateNameEng: string | null | undefined,
|
||||
description: string | null | undefined,
|
||||
userId: string
|
||||
): Promise<string> {
|
||||
return transaction(async (client) => {
|
||||
// 리포트 정보 조회
|
||||
const reportQuery = `
|
||||
SELECT report_type FROM report_master WHERE report_id = $1
|
||||
`;
|
||||
const reportResult = await client.query(reportQuery, [reportId]);
|
||||
|
||||
if (reportResult.rows.length === 0) {
|
||||
throw new Error("리포트를 찾을 수 없습니다.");
|
||||
}
|
||||
|
||||
const reportType = reportResult.rows[0].report_type;
|
||||
|
||||
// 레이아웃 조회
|
||||
const layoutQuery = `
|
||||
SELECT
|
||||
canvas_width,
|
||||
canvas_height,
|
||||
page_orientation,
|
||||
margin_top,
|
||||
margin_bottom,
|
||||
margin_left,
|
||||
margin_right,
|
||||
components
|
||||
FROM report_layout
|
||||
WHERE report_id = $1
|
||||
`;
|
||||
const layoutResult = await client.query(layoutQuery, [reportId]);
|
||||
|
||||
if (layoutResult.rows.length === 0) {
|
||||
throw new Error("레이아웃을 찾을 수 없습니다.");
|
||||
}
|
||||
|
||||
const layout = layoutResult.rows[0];
|
||||
|
||||
// 쿼리 조회
|
||||
const queriesQuery = `
|
||||
SELECT
|
||||
query_name,
|
||||
query_type,
|
||||
sql_query,
|
||||
parameters,
|
||||
external_connection_id,
|
||||
display_order
|
||||
FROM report_query
|
||||
WHERE report_id = $1
|
||||
ORDER BY display_order
|
||||
`;
|
||||
const queriesResult = await client.query(queriesQuery, [reportId]);
|
||||
|
||||
// 레이아웃 설정 JSON 생성
|
||||
const layoutConfig = {
|
||||
width: layout.canvas_width,
|
||||
height: layout.canvas_height,
|
||||
orientation: layout.page_orientation,
|
||||
margins: {
|
||||
top: layout.margin_top,
|
||||
bottom: layout.margin_bottom,
|
||||
left: layout.margin_left,
|
||||
right: layout.margin_right,
|
||||
},
|
||||
components: JSON.parse(layout.components || "[]"),
|
||||
};
|
||||
|
||||
// 기본 쿼리 JSON 생성
|
||||
const defaultQueries = queriesResult.rows.map((q) => ({
|
||||
name: q.query_name,
|
||||
type: q.query_type,
|
||||
sqlQuery: q.sql_query,
|
||||
parameters: Array.isArray(q.parameters) ? q.parameters : [],
|
||||
externalConnectionId: q.external_connection_id,
|
||||
displayOrder: q.display_order,
|
||||
}));
|
||||
|
||||
// 템플릿 생성
|
||||
const templateId = `TPL_${uuidv4().replace(/-/g, "").substring(0, 20)}`;
|
||||
|
||||
const insertQuery = `
|
||||
INSERT INTO report_template (
|
||||
template_id,
|
||||
template_name_kor,
|
||||
template_name_eng,
|
||||
template_type,
|
||||
is_system,
|
||||
description,
|
||||
layout_config,
|
||||
default_queries,
|
||||
use_yn,
|
||||
sort_order,
|
||||
created_by
|
||||
) VALUES ($1, $2, $3, $4, 'N', $5, $6, $7, 'Y', 999, $8)
|
||||
`;
|
||||
|
||||
await client.query(insertQuery, [
|
||||
templateId,
|
||||
templateNameKor,
|
||||
templateNameEng || null,
|
||||
reportType,
|
||||
description || null,
|
||||
JSON.stringify(layoutConfig),
|
||||
JSON.stringify(defaultQueries),
|
||||
userId,
|
||||
]);
|
||||
|
||||
return templateId;
|
||||
});
|
||||
}
|
||||
|
||||
// 레이아웃 데이터로 직접 템플릿 생성 (리포트 저장 불필요)
|
||||
async createTemplateFromLayout(
|
||||
templateNameKor: string,
|
||||
templateNameEng: string | null | undefined,
|
||||
templateType: string,
|
||||
description: string | null | undefined,
|
||||
layoutConfig: {
|
||||
width: number;
|
||||
height: number;
|
||||
orientation: string;
|
||||
margins: {
|
||||
top: number;
|
||||
bottom: number;
|
||||
left: number;
|
||||
right: number;
|
||||
};
|
||||
components: any[];
|
||||
},
|
||||
defaultQueries: Array<{
|
||||
name: string;
|
||||
type: "MASTER" | "DETAIL";
|
||||
sqlQuery: string;
|
||||
parameters: string[];
|
||||
externalConnectionId?: number | null;
|
||||
displayOrder?: number;
|
||||
}>,
|
||||
userId: string
|
||||
): Promise<string> {
|
||||
const templateId = `TPL_${uuidv4().replace(/-/g, "").substring(0, 20)}`;
|
||||
|
||||
const insertQuery = `
|
||||
INSERT INTO report_template (
|
||||
template_id,
|
||||
template_name_kor,
|
||||
template_name_eng,
|
||||
template_type,
|
||||
is_system,
|
||||
description,
|
||||
layout_config,
|
||||
default_queries,
|
||||
use_yn,
|
||||
sort_order,
|
||||
created_by
|
||||
) VALUES ($1, $2, $3, $4, 'N', $5, $6, $7, 'Y', 999, $8)
|
||||
RETURNING template_id
|
||||
`;
|
||||
|
||||
await query(insertQuery, [
|
||||
templateId,
|
||||
templateNameKor,
|
||||
templateNameEng || null,
|
||||
templateType,
|
||||
description || null,
|
||||
JSON.stringify(layoutConfig),
|
||||
JSON.stringify(defaultQueries),
|
||||
userId,
|
||||
]);
|
||||
|
||||
return templateId;
|
||||
}
|
||||
}
|
||||
|
||||
export default new ReportService();
|
||||
|
||||
Reference in New Issue
Block a user