회사별 메뉴 분리 및 권한 관리

This commit is contained in:
kjs
2025-10-28 10:07:07 +09:00
parent 35581ac8d2
commit 25f6217433
13 changed files with 1273 additions and 370 deletions

View File

@@ -21,14 +21,22 @@ export class CommonCodeController {
async getCategories(req: AuthenticatedRequest, res: Response) {
try {
const { search, isActive, page = "1", size = "20" } = req.query;
const userCompanyCode = req.user?.companyCode;
const categories = await this.commonCodeService.getCategories({
search: search as string,
isActive:
isActive === "true" ? true : isActive === "false" ? false : undefined,
page: parseInt(page as string),
size: parseInt(size as string),
});
const categories = await this.commonCodeService.getCategories(
{
search: search as string,
isActive:
isActive === "true"
? true
: isActive === "false"
? false
: undefined,
page: parseInt(page as string),
size: parseInt(size as string),
},
userCompanyCode
);
return res.json({
success: true,
@@ -54,14 +62,23 @@ export class CommonCodeController {
try {
const { categoryCode } = req.params;
const { search, isActive, page, size } = req.query;
const userCompanyCode = req.user?.companyCode;
const result = await this.commonCodeService.getCodes(categoryCode, {
search: search as string,
isActive:
isActive === "true" ? true : isActive === "false" ? false : undefined,
page: page ? parseInt(page as string) : undefined,
size: size ? parseInt(size as string) : undefined,
});
const result = await this.commonCodeService.getCodes(
categoryCode,
{
search: search as string,
isActive:
isActive === "true"
? true
: isActive === "false"
? false
: undefined,
page: page ? parseInt(page as string) : undefined,
size: size ? parseInt(size as string) : undefined,
},
userCompanyCode
);
// 프론트엔드가 기대하는 형식으로 데이터 변환
const transformedData = result.data.map((code: any) => ({
@@ -73,7 +90,8 @@ export class CommonCodeController {
sortOrder: code.sort_order,
isActive: code.is_active,
useYn: code.is_active,
companyCode: code.company_code, // 추가
// 기존 필드명도 유지 (하위 호환성)
code_category: code.code_category,
code_value: code.code_value,
@@ -81,6 +99,7 @@ export class CommonCodeController {
code_name_eng: code.code_name_eng,
sort_order: code.sort_order,
is_active: code.is_active,
company_code: code.company_code, // 추가
created_date: code.created_date,
created_by: code.created_by,
updated_date: code.updated_date,
@@ -110,7 +129,8 @@ export class CommonCodeController {
async createCategory(req: AuthenticatedRequest, res: Response) {
try {
const categoryData: CreateCategoryData = req.body;
const userId = req.user?.userId || "SYSTEM"; // 인증 미들웨어에서 설정된 사용자 ID
const userId = req.user?.userId || "SYSTEM";
const companyCode = req.user?.companyCode || "*";
// 입력값 검증
if (!categoryData.categoryCode || !categoryData.categoryName) {
@@ -122,7 +142,8 @@ export class CommonCodeController {
const category = await this.commonCodeService.createCategory(
categoryData,
userId
userId,
companyCode
);
return res.status(201).json({
@@ -135,7 +156,7 @@ export class CommonCodeController {
// PostgreSQL 에러 처리
if (
((error as any)?.code === "23505") || // PostgreSQL unique_violation
(error as any)?.code === "23505" || // PostgreSQL unique_violation
(error instanceof Error && error.message.includes("Unique constraint"))
) {
return res.status(409).json({
@@ -161,11 +182,13 @@ export class CommonCodeController {
const { categoryCode } = req.params;
const categoryData: Partial<CreateCategoryData> = req.body;
const userId = req.user?.userId || "SYSTEM";
const companyCode = req.user?.companyCode;
const category = await this.commonCodeService.updateCategory(
categoryCode,
categoryData,
userId
userId,
companyCode
);
return res.json({
@@ -201,8 +224,9 @@ export class CommonCodeController {
async deleteCategory(req: AuthenticatedRequest, res: Response) {
try {
const { categoryCode } = req.params;
const companyCode = req.user?.companyCode;
await this.commonCodeService.deleteCategory(categoryCode);
await this.commonCodeService.deleteCategory(categoryCode, companyCode);
return res.json({
success: true,
@@ -238,6 +262,7 @@ export class CommonCodeController {
const { categoryCode } = req.params;
const codeData: CreateCodeData = req.body;
const userId = req.user?.userId || "SYSTEM";
const companyCode = req.user?.companyCode || "*";
// 입력값 검증
if (!codeData.codeValue || !codeData.codeName) {
@@ -250,7 +275,8 @@ export class CommonCodeController {
const code = await this.commonCodeService.createCode(
categoryCode,
codeData,
userId
userId,
companyCode
);
return res.status(201).json({
@@ -288,12 +314,14 @@ export class CommonCodeController {
const { categoryCode, codeValue } = req.params;
const codeData: Partial<CreateCodeData> = req.body;
const userId = req.user?.userId || "SYSTEM";
const companyCode = req.user?.companyCode;
const code = await this.commonCodeService.updateCode(
categoryCode,
codeValue,
codeData,
userId
userId,
companyCode
);
return res.json({
@@ -332,8 +360,13 @@ export class CommonCodeController {
async deleteCode(req: AuthenticatedRequest, res: Response) {
try {
const { categoryCode, codeValue } = req.params;
const companyCode = req.user?.companyCode;
await this.commonCodeService.deleteCode(categoryCode, codeValue);
await this.commonCodeService.deleteCode(
categoryCode,
codeValue,
companyCode
);
return res.json({
success: true,
@@ -370,8 +403,12 @@ export class CommonCodeController {
async getCodeOptions(req: AuthenticatedRequest, res: Response) {
try {
const { categoryCode } = req.params;
const userCompanyCode = req.user?.companyCode;
const options = await this.commonCodeService.getCodeOptions(categoryCode);
const options = await this.commonCodeService.getCodeOptions(
categoryCode,
userCompanyCode
);
return res.json({
success: true,

View File

@@ -383,6 +383,79 @@ export class DDLController {
}
}
/**
* DELETE /api/ddl/tables/:tableName - 테이블 삭제 (최고 관리자 전용)
*/
static async dropTable(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const { tableName } = req.params;
const userId = req.user!.userId;
const userCompanyCode = req.user!.companyCode;
// 입력값 기본 검증
if (!tableName) {
res.status(400).json({
success: false,
error: {
code: "INVALID_INPUT",
details: "테이블명이 필요합니다.",
},
});
return;
}
logger.info("테이블 삭제 요청", {
tableName,
userId,
userCompanyCode,
ip: req.ip,
});
// DDL 실행 서비스 호출
const ddlService = new DDLExecutionService();
const result = await ddlService.dropTable(
tableName,
userCompanyCode,
userId
);
if (result.success) {
res.status(200).json({
success: true,
message: result.message,
data: {
tableName,
executedQuery: result.executedQuery,
},
});
} else {
res.status(400).json({
success: false,
message: result.message,
error: result.error,
});
}
} catch (error) {
logger.error("테이블 삭제 컨트롤러 오류:", {
error: (error as Error).message,
stack: (error as Error).stack,
userId: req.user?.userId,
tableName: req.params.tableName,
});
res.status(500).json({
success: false,
error: {
code: "INTERNAL_SERVER_ERROR",
details: "테이블 삭제 중 서버 오류가 발생했습니다.",
},
});
}
}
/**
* DELETE /api/ddl/logs/cleanup - 오래된 DDL 로그 정리
*/

View File

@@ -551,6 +551,76 @@ export class FlowController {
}
};
/**
* 플로우 스텝의 컬럼 라벨 조회
*/
getStepColumnLabels = async (req: Request, res: Response): Promise<void> => {
try {
const { flowId, stepId } = req.params;
const step = await this.flowStepService.getById(parseInt(stepId));
if (!step) {
res.status(404).json({
success: false,
message: "Step not found",
});
return;
}
const flowDef = await this.flowDefinitionService.getById(
parseInt(flowId)
);
if (!flowDef) {
res.status(404).json({
success: false,
message: "Flow definition not found",
});
return;
}
// 테이블명 결정 (스텝 테이블 우선, 없으면 플로우 테이블)
const tableName = step.tableName || flowDef.tableName;
if (!tableName) {
res.json({
success: true,
data: {},
});
return;
}
// column_labels 테이블에서 라벨 정보 조회
const { query } = await import("../config/database");
const labelRows = await query<{
column_name: string;
column_label: string | null;
}>(
`SELECT column_name, column_label
FROM column_labels
WHERE table_name = $1 AND column_label IS NOT NULL`,
[tableName]
);
// { columnName: label } 형태의 객체로 변환
const labels: Record<string, string> = {};
labelRows.forEach((row) => {
if (row.column_label) {
labels[row.column_name] = row.column_label;
}
});
res.json({
success: true,
data: labels,
});
} catch (error: any) {
console.error("Error getting step column labels:", error);
res.status(500).json({
success: false,
message: error.message || "Failed to get step column labels",
});
}
};
/**
* 플로우의 모든 단계별 카운트 조회
*/