테이블 관계 저장 구현
This commit is contained in:
@@ -18,6 +18,7 @@ import commonCodeRoutes from "./routes/commonCodeRoutes";
|
||||
import dynamicFormRoutes from "./routes/dynamicFormRoutes";
|
||||
import fileRoutes from "./routes/fileRoutes";
|
||||
import companyManagementRoutes from "./routes/companyManagementRoutes";
|
||||
import dataflowRoutes from "./routes/dataflowRoutes";
|
||||
// import userRoutes from './routes/userRoutes';
|
||||
// import menuRoutes from './routes/menuRoutes';
|
||||
|
||||
@@ -83,6 +84,7 @@ app.use("/api/common-codes", commonCodeRoutes);
|
||||
app.use("/api/dynamic-form", dynamicFormRoutes);
|
||||
app.use("/api/files", fileRoutes);
|
||||
app.use("/api/company-management", companyManagementRoutes);
|
||||
app.use("/api/dataflow", dataflowRoutes);
|
||||
// app.use('/api/users', userRoutes);
|
||||
// app.use('/api/menus', menuRoutes);
|
||||
|
||||
|
||||
553
backend-node/src/controllers/dataflowController.ts
Normal file
553
backend-node/src/controllers/dataflowController.ts
Normal file
@@ -0,0 +1,553 @@
|
||||
import { Request, Response } from "express";
|
||||
import { logger } from "../utils/logger";
|
||||
import { AuthenticatedRequest } from "../types/auth";
|
||||
import { ApiResponse } from "../types/common";
|
||||
import { DataflowService } from "../services/dataflowService";
|
||||
|
||||
/**
|
||||
* 테이블 관계 생성
|
||||
*/
|
||||
export async function createTableRelationship(
|
||||
req: AuthenticatedRequest,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
try {
|
||||
logger.info("=== 테이블 관계 생성 시작 ===");
|
||||
|
||||
const {
|
||||
relationshipName,
|
||||
fromTableName,
|
||||
fromColumnName,
|
||||
toTableName,
|
||||
toColumnName,
|
||||
relationshipType,
|
||||
connectionType,
|
||||
settings,
|
||||
} = req.body;
|
||||
|
||||
// 필수 필드 검증
|
||||
if (
|
||||
!relationshipName ||
|
||||
!fromTableName ||
|
||||
!fromColumnName ||
|
||||
!toTableName ||
|
||||
!toColumnName
|
||||
) {
|
||||
const response: ApiResponse<null> = {
|
||||
success: false,
|
||||
message: "필수 필드가 누락되었습니다.",
|
||||
error: {
|
||||
code: "MISSING_REQUIRED_FIELDS",
|
||||
details:
|
||||
"relationshipName, fromTableName, fromColumnName, toTableName, toColumnName는 필수입니다.",
|
||||
},
|
||||
};
|
||||
res.status(400).json(response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 사용자 정보에서 회사 코드 가져오기
|
||||
const companyCode = (req.user as any)?.company_code || "*";
|
||||
const userId = (req.user as any)?.userId || "system";
|
||||
|
||||
const dataflowService = new DataflowService();
|
||||
const relationship = await dataflowService.createTableRelationship({
|
||||
relationshipName,
|
||||
fromTableName,
|
||||
fromColumnName,
|
||||
toTableName,
|
||||
toColumnName,
|
||||
relationshipType: relationshipType || "one-to-one",
|
||||
connectionType: connectionType || "simple-key",
|
||||
companyCode,
|
||||
settings: settings || {},
|
||||
createdBy: userId,
|
||||
});
|
||||
|
||||
logger.info(`테이블 관계 생성 완료: ${relationship.relationship_id}`);
|
||||
|
||||
const response: ApiResponse<any> = {
|
||||
success: true,
|
||||
message: "테이블 관계가 성공적으로 생성되었습니다.",
|
||||
data: relationship,
|
||||
};
|
||||
|
||||
res.status(201).json(response);
|
||||
} catch (error) {
|
||||
logger.error("테이블 관계 생성 중 오류 발생:", error);
|
||||
|
||||
const response: ApiResponse<null> = {
|
||||
success: false,
|
||||
message: "테이블 관계 생성 중 오류가 발생했습니다.",
|
||||
error: {
|
||||
code: "TABLE_RELATIONSHIP_CREATE_ERROR",
|
||||
details: error instanceof Error ? error.message : "Unknown error",
|
||||
},
|
||||
};
|
||||
|
||||
res.status(500).json(response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 테이블 관계 목록 조회 (회사별)
|
||||
*/
|
||||
export async function getTableRelationships(
|
||||
req: AuthenticatedRequest,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
try {
|
||||
logger.info("=== 테이블 관계 목록 조회 시작 ===");
|
||||
|
||||
// 사용자 정보에서 회사 코드 가져오기
|
||||
const companyCode = (req.user as any)?.company_code || "*";
|
||||
|
||||
const dataflowService = new DataflowService();
|
||||
const relationships =
|
||||
await dataflowService.getTableRelationships(companyCode);
|
||||
|
||||
logger.info(`테이블 관계 목록 조회 완료: ${relationships.length}개`);
|
||||
|
||||
const response: ApiResponse<any[]> = {
|
||||
success: true,
|
||||
message: "테이블 관계 목록을 성공적으로 조회했습니다.",
|
||||
data: relationships,
|
||||
};
|
||||
|
||||
res.status(200).json(response);
|
||||
} catch (error) {
|
||||
logger.error("테이블 관계 목록 조회 중 오류 발생:", error);
|
||||
|
||||
const response: ApiResponse<null> = {
|
||||
success: false,
|
||||
message: "테이블 관계 목록 조회 중 오류가 발생했습니다.",
|
||||
error: {
|
||||
code: "TABLE_RELATIONSHIPS_LIST_ERROR",
|
||||
details: error instanceof Error ? error.message : "Unknown error",
|
||||
},
|
||||
};
|
||||
|
||||
res.status(500).json(response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 테이블 관계 수정
|
||||
*/
|
||||
export async function updateTableRelationship(
|
||||
req: AuthenticatedRequest,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
try {
|
||||
logger.info("=== 테이블 관계 수정 시작 ===");
|
||||
|
||||
const { relationshipId } = req.params;
|
||||
const updateData = req.body;
|
||||
|
||||
if (!relationshipId) {
|
||||
const response: ApiResponse<null> = {
|
||||
success: false,
|
||||
message: "관계 ID가 필요합니다.",
|
||||
error: {
|
||||
code: "MISSING_RELATIONSHIP_ID",
|
||||
details: "relationshipId 파라미터가 누락되었습니다.",
|
||||
},
|
||||
};
|
||||
res.status(400).json(response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 사용자 정보에서 회사 코드와 사용자 ID 가져오기
|
||||
const companyCode = (req.user as any)?.company_code || "*";
|
||||
const userId = (req.user as any)?.userId || "system";
|
||||
|
||||
const dataflowService = new DataflowService();
|
||||
const relationship = await dataflowService.updateTableRelationship(
|
||||
parseInt(relationshipId),
|
||||
{
|
||||
...updateData,
|
||||
updatedBy: userId,
|
||||
},
|
||||
companyCode
|
||||
);
|
||||
|
||||
if (!relationship) {
|
||||
const response: ApiResponse<null> = {
|
||||
success: false,
|
||||
message: "테이블 관계를 찾을 수 없습니다.",
|
||||
error: {
|
||||
code: "TABLE_RELATIONSHIP_NOT_FOUND",
|
||||
details: `관계 ID ${relationshipId}를 찾을 수 없습니다.`,
|
||||
},
|
||||
};
|
||||
res.status(404).json(response);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info(`테이블 관계 수정 완료: ${relationshipId}`);
|
||||
|
||||
const response: ApiResponse<any> = {
|
||||
success: true,
|
||||
message: "테이블 관계가 성공적으로 수정되었습니다.",
|
||||
data: relationship,
|
||||
};
|
||||
|
||||
res.status(200).json(response);
|
||||
} catch (error) {
|
||||
logger.error("테이블 관계 수정 중 오류 발생:", error);
|
||||
|
||||
const response: ApiResponse<null> = {
|
||||
success: false,
|
||||
message: "테이블 관계 수정 중 오류가 발생했습니다.",
|
||||
error: {
|
||||
code: "TABLE_RELATIONSHIP_UPDATE_ERROR",
|
||||
details: error instanceof Error ? error.message : "Unknown error",
|
||||
},
|
||||
};
|
||||
|
||||
res.status(500).json(response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 테이블 관계 삭제
|
||||
*/
|
||||
export async function deleteTableRelationship(
|
||||
req: AuthenticatedRequest,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
try {
|
||||
logger.info("=== 테이블 관계 삭제 시작 ===");
|
||||
|
||||
const { relationshipId } = req.params;
|
||||
|
||||
if (!relationshipId) {
|
||||
const response: ApiResponse<null> = {
|
||||
success: false,
|
||||
message: "관계 ID가 필요합니다.",
|
||||
error: {
|
||||
code: "MISSING_RELATIONSHIP_ID",
|
||||
details: "relationshipId 파라미터가 누락되었습니다.",
|
||||
},
|
||||
};
|
||||
res.status(400).json(response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 사용자 정보에서 회사 코드 가져오기
|
||||
const companyCode = (req.user as any)?.company_code || "*";
|
||||
|
||||
const dataflowService = new DataflowService();
|
||||
const success = await dataflowService.deleteTableRelationship(
|
||||
parseInt(relationshipId),
|
||||
companyCode
|
||||
);
|
||||
|
||||
if (!success) {
|
||||
const response: ApiResponse<null> = {
|
||||
success: false,
|
||||
message: "테이블 관계를 찾을 수 없습니다.",
|
||||
error: {
|
||||
code: "TABLE_RELATIONSHIP_NOT_FOUND",
|
||||
details: `관계 ID ${relationshipId}를 찾을 수 없습니다.`,
|
||||
},
|
||||
};
|
||||
res.status(404).json(response);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info(`테이블 관계 삭제 완료: ${relationshipId}`);
|
||||
|
||||
const response: ApiResponse<null> = {
|
||||
success: true,
|
||||
message: "테이블 관계가 성공적으로 삭제되었습니다.",
|
||||
};
|
||||
|
||||
res.status(200).json(response);
|
||||
} catch (error) {
|
||||
logger.error("테이블 관계 삭제 중 오류 발생:", error);
|
||||
|
||||
const response: ApiResponse<null> = {
|
||||
success: false,
|
||||
message: "테이블 관계 삭제 중 오류가 발생했습니다.",
|
||||
error: {
|
||||
code: "TABLE_RELATIONSHIP_DELETE_ERROR",
|
||||
details: error instanceof Error ? error.message : "Unknown error",
|
||||
},
|
||||
};
|
||||
|
||||
res.status(500).json(response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 테이블 관계 조회
|
||||
*/
|
||||
export async function getTableRelationship(
|
||||
req: AuthenticatedRequest,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
try {
|
||||
logger.info("=== 테이블 관계 조회 시작 ===");
|
||||
|
||||
const { relationshipId } = req.params;
|
||||
|
||||
if (!relationshipId) {
|
||||
const response: ApiResponse<null> = {
|
||||
success: false,
|
||||
message: "관계 ID가 필요합니다.",
|
||||
error: {
|
||||
code: "MISSING_RELATIONSHIP_ID",
|
||||
details: "relationshipId 파라미터가 누락되었습니다.",
|
||||
},
|
||||
};
|
||||
res.status(400).json(response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 사용자 정보에서 회사 코드 가져오기
|
||||
const companyCode = (req.user as any)?.company_code || "*";
|
||||
|
||||
const dataflowService = new DataflowService();
|
||||
const relationship = await dataflowService.getTableRelationship(
|
||||
parseInt(relationshipId),
|
||||
companyCode
|
||||
);
|
||||
|
||||
if (!relationship) {
|
||||
const response: ApiResponse<null> = {
|
||||
success: false,
|
||||
message: "테이블 관계를 찾을 수 없습니다.",
|
||||
error: {
|
||||
code: "TABLE_RELATIONSHIP_NOT_FOUND",
|
||||
details: `관계 ID ${relationshipId}를 찾을 수 없습니다.`,
|
||||
},
|
||||
};
|
||||
res.status(404).json(response);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info(`테이블 관계 조회 완료: ${relationshipId}`);
|
||||
|
||||
const response: ApiResponse<any> = {
|
||||
success: true,
|
||||
message: "테이블 관계를 성공적으로 조회했습니다.",
|
||||
data: relationship,
|
||||
};
|
||||
|
||||
res.status(200).json(response);
|
||||
} catch (error) {
|
||||
logger.error("테이블 관계 조회 중 오류 발생:", error);
|
||||
|
||||
const response: ApiResponse<null> = {
|
||||
success: false,
|
||||
message: "테이블 관계 조회 중 오류가 발생했습니다.",
|
||||
error: {
|
||||
code: "TABLE_RELATIONSHIP_GET_ERROR",
|
||||
details: error instanceof Error ? error.message : "Unknown error",
|
||||
},
|
||||
};
|
||||
|
||||
res.status(500).json(response);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 데이터 연결 관리 API ====================
|
||||
|
||||
/**
|
||||
* 데이터 관계 연결 생성
|
||||
*/
|
||||
export async function createDataLink(
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
try {
|
||||
const {
|
||||
relationshipId,
|
||||
fromTableName,
|
||||
fromColumnName,
|
||||
fromKeyValue,
|
||||
fromRecordId,
|
||||
toTableName,
|
||||
toColumnName,
|
||||
toKeyValue,
|
||||
toRecordId,
|
||||
connectionType,
|
||||
bridgeData,
|
||||
} = req.body;
|
||||
|
||||
// 필수 필드 검증
|
||||
if (
|
||||
!relationshipId ||
|
||||
!fromTableName ||
|
||||
!fromColumnName ||
|
||||
!fromKeyValue ||
|
||||
!toTableName ||
|
||||
!toColumnName ||
|
||||
!toKeyValue ||
|
||||
!connectionType
|
||||
) {
|
||||
const response: ApiResponse<null> = {
|
||||
success: false,
|
||||
message: "필수 필드가 누락되었습니다.",
|
||||
error: {
|
||||
code: "MISSING_REQUIRED_FIELDS",
|
||||
details:
|
||||
"필수 필드: relationshipId, fromTableName, fromColumnName, fromKeyValue, toTableName, toColumnName, toKeyValue, connectionType",
|
||||
},
|
||||
};
|
||||
res.status(400).json(response);
|
||||
return;
|
||||
}
|
||||
|
||||
const userInfo = (req as any).user;
|
||||
const companyCode = userInfo?.company_code || "*";
|
||||
const createdBy = userInfo?.userId || "system";
|
||||
|
||||
const dataflowService = new DataflowService();
|
||||
const bridge = await dataflowService.createDataLink({
|
||||
relationshipId,
|
||||
fromTableName,
|
||||
fromColumnName,
|
||||
fromKeyValue,
|
||||
fromRecordId,
|
||||
toTableName,
|
||||
toColumnName,
|
||||
toKeyValue,
|
||||
toRecordId,
|
||||
connectionType,
|
||||
companyCode,
|
||||
bridgeData,
|
||||
createdBy,
|
||||
});
|
||||
|
||||
const response: ApiResponse<typeof bridge> = {
|
||||
success: true,
|
||||
message: "데이터 연결이 성공적으로 생성되었습니다.",
|
||||
data: bridge,
|
||||
};
|
||||
|
||||
res.status(201).json(response);
|
||||
} catch (error) {
|
||||
logger.error("데이터 연결 생성 중 오류 발생:", error);
|
||||
|
||||
const response: ApiResponse<null> = {
|
||||
success: false,
|
||||
message: "데이터 연결 생성 중 오류가 발생했습니다.",
|
||||
error: {
|
||||
code: "DATA_LINK_CREATE_ERROR",
|
||||
details: error instanceof Error ? error.message : "Unknown error",
|
||||
},
|
||||
};
|
||||
|
||||
res.status(500).json(response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 관계별 연결된 데이터 조회
|
||||
*/
|
||||
export async function getLinkedDataByRelationship(
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
try {
|
||||
const relationshipId = parseInt(req.params.relationshipId);
|
||||
|
||||
if (!relationshipId || isNaN(relationshipId)) {
|
||||
const response: ApiResponse<null> = {
|
||||
success: false,
|
||||
message: "유효하지 않은 관계 ID입니다.",
|
||||
error: {
|
||||
code: "INVALID_RELATIONSHIP_ID",
|
||||
details: "관계 ID는 숫자여야 합니다.",
|
||||
},
|
||||
};
|
||||
res.status(400).json(response);
|
||||
return;
|
||||
}
|
||||
|
||||
const userInfo = (req as any).user;
|
||||
const companyCode = userInfo?.company_code || "*";
|
||||
|
||||
const dataflowService = new DataflowService();
|
||||
const linkedData = await dataflowService.getLinkedDataByRelationship(
|
||||
relationshipId,
|
||||
companyCode
|
||||
);
|
||||
|
||||
const response: ApiResponse<typeof linkedData> = {
|
||||
success: true,
|
||||
message: "연결된 데이터를 성공적으로 조회했습니다.",
|
||||
data: linkedData,
|
||||
};
|
||||
|
||||
res.status(200).json(response);
|
||||
} catch (error) {
|
||||
logger.error("연결된 데이터 조회 중 오류 발생:", error);
|
||||
|
||||
const response: ApiResponse<null> = {
|
||||
success: false,
|
||||
message: "연결된 데이터 조회 중 오류가 발생했습니다.",
|
||||
error: {
|
||||
code: "LINKED_DATA_GET_ERROR",
|
||||
details: error instanceof Error ? error.message : "Unknown error",
|
||||
},
|
||||
};
|
||||
|
||||
res.status(500).json(response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터 연결 삭제
|
||||
*/
|
||||
export async function deleteDataLink(
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
try {
|
||||
const bridgeId = parseInt(req.params.bridgeId);
|
||||
|
||||
if (!bridgeId || isNaN(bridgeId)) {
|
||||
const response: ApiResponse<null> = {
|
||||
success: false,
|
||||
message: "유효하지 않은 Bridge ID입니다.",
|
||||
error: {
|
||||
code: "INVALID_BRIDGE_ID",
|
||||
details: "Bridge ID는 숫자여야 합니다.",
|
||||
},
|
||||
};
|
||||
res.status(400).json(response);
|
||||
return;
|
||||
}
|
||||
|
||||
const userInfo = (req as any).user;
|
||||
const companyCode = userInfo?.company_code || "*";
|
||||
const deletedBy = userInfo?.userId || "system";
|
||||
|
||||
const dataflowService = new DataflowService();
|
||||
await dataflowService.deleteDataLink(bridgeId, companyCode, deletedBy);
|
||||
|
||||
const response: ApiResponse<null> = {
|
||||
success: true,
|
||||
message: "데이터 연결이 성공적으로 삭제되었습니다.",
|
||||
data: null,
|
||||
};
|
||||
|
||||
res.status(200).json(response);
|
||||
} catch (error) {
|
||||
logger.error("데이터 연결 삭제 중 오류 발생:", error);
|
||||
|
||||
const response: ApiResponse<null> = {
|
||||
success: false,
|
||||
message: "데이터 연결 삭제 중 오류가 발생했습니다.",
|
||||
error: {
|
||||
code: "DATA_LINK_DELETE_ERROR",
|
||||
details: error instanceof Error ? error.message : "Unknown error",
|
||||
},
|
||||
};
|
||||
|
||||
res.status(500).json(response);
|
||||
}
|
||||
}
|
||||
72
backend-node/src/routes/dataflowRoutes.ts
Normal file
72
backend-node/src/routes/dataflowRoutes.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import express from "express";
|
||||
import { authenticateToken } from "../middleware/authMiddleware";
|
||||
import {
|
||||
createTableRelationship,
|
||||
getTableRelationships,
|
||||
getTableRelationship,
|
||||
updateTableRelationship,
|
||||
deleteTableRelationship,
|
||||
createDataLink,
|
||||
getLinkedDataByRelationship,
|
||||
deleteDataLink,
|
||||
} from "../controllers/dataflowController";
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// 모든 라우트에 인증 미들웨어 적용
|
||||
router.use(authenticateToken);
|
||||
|
||||
/**
|
||||
* 테이블 관계 생성
|
||||
* POST /api/dataflow/table-relationships
|
||||
*/
|
||||
router.post("/table-relationships", createTableRelationship);
|
||||
|
||||
/**
|
||||
* 테이블 관계 목록 조회 (회사별)
|
||||
* GET /api/dataflow/table-relationships
|
||||
*/
|
||||
router.get("/table-relationships", getTableRelationships);
|
||||
|
||||
/**
|
||||
* 특정 테이블 관계 조회
|
||||
* GET /api/dataflow/table-relationships/:relationshipId
|
||||
*/
|
||||
router.get("/table-relationships/:relationshipId", getTableRelationship);
|
||||
|
||||
/**
|
||||
* 테이블 관계 수정
|
||||
* PUT /api/dataflow/table-relationships/:relationshipId
|
||||
*/
|
||||
router.put("/table-relationships/:relationshipId", updateTableRelationship);
|
||||
|
||||
/**
|
||||
* 테이블 관계 삭제
|
||||
* DELETE /api/dataflow/table-relationships/:relationshipId
|
||||
*/
|
||||
router.delete("/table-relationships/:relationshipId", deleteTableRelationship);
|
||||
|
||||
// ==================== 데이터 연결 관리 라우트 ====================
|
||||
|
||||
/**
|
||||
* 데이터 연결 생성
|
||||
* POST /api/dataflow/data-links
|
||||
*/
|
||||
router.post("/data-links", createDataLink);
|
||||
|
||||
/**
|
||||
* 관계별 연결된 데이터 조회
|
||||
* GET /api/dataflow/data-links/relationship/:relationshipId
|
||||
*/
|
||||
router.get(
|
||||
"/data-links/relationship/:relationshipId",
|
||||
getLinkedDataByRelationship
|
||||
);
|
||||
|
||||
/**
|
||||
* 데이터 연결 삭제
|
||||
* DELETE /api/dataflow/data-links/:bridgeId
|
||||
*/
|
||||
router.delete("/data-links/:bridgeId", deleteDataLink);
|
||||
|
||||
export default router;
|
||||
665
backend-node/src/services/dataflowService.ts
Normal file
665
backend-node/src/services/dataflowService.ts
Normal file
@@ -0,0 +1,665 @@
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import { logger } from "../utils/logger";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
// 테이블 관계 생성 데이터 타입
|
||||
interface CreateTableRelationshipData {
|
||||
relationshipName: string;
|
||||
fromTableName: string;
|
||||
fromColumnName: string;
|
||||
toTableName: string;
|
||||
toColumnName: string;
|
||||
relationshipType: string;
|
||||
connectionType: string;
|
||||
companyCode: string;
|
||||
settings: any;
|
||||
createdBy: string;
|
||||
}
|
||||
|
||||
// 테이블 관계 수정 데이터 타입
|
||||
interface UpdateTableRelationshipData {
|
||||
relationshipName?: string;
|
||||
fromTableName?: string;
|
||||
fromColumnName?: string;
|
||||
toTableName?: string;
|
||||
toColumnName?: string;
|
||||
relationshipType?: string;
|
||||
connectionType?: string;
|
||||
settings?: any;
|
||||
updatedBy: string;
|
||||
}
|
||||
|
||||
export class DataflowService {
|
||||
/**
|
||||
* 테이블 관계 생성
|
||||
*/
|
||||
async createTableRelationship(data: CreateTableRelationshipData) {
|
||||
try {
|
||||
logger.info("DataflowService: 테이블 관계 생성 시작", data);
|
||||
|
||||
// 중복 관계 확인
|
||||
const existingRelationship = await prisma.table_relationships.findFirst({
|
||||
where: {
|
||||
from_table_name: data.fromTableName,
|
||||
from_column_name: data.fromColumnName,
|
||||
to_table_name: data.toTableName,
|
||||
to_column_name: data.toColumnName,
|
||||
company_code: data.companyCode,
|
||||
is_active: "Y",
|
||||
},
|
||||
});
|
||||
|
||||
if (existingRelationship) {
|
||||
throw new Error(
|
||||
`이미 존재하는 관계입니다: ${data.fromTableName}.${data.fromColumnName} → ${data.toTableName}.${data.toColumnName}`
|
||||
);
|
||||
}
|
||||
|
||||
// 새 관계 생성
|
||||
const relationship = await prisma.table_relationships.create({
|
||||
data: {
|
||||
relationship_name: data.relationshipName,
|
||||
from_table_name: data.fromTableName,
|
||||
from_column_name: data.fromColumnName,
|
||||
to_table_name: data.toTableName,
|
||||
to_column_name: data.toColumnName,
|
||||
relationship_type: data.relationshipType,
|
||||
connection_type: data.connectionType,
|
||||
company_code: data.companyCode,
|
||||
settings: data.settings,
|
||||
created_by: data.createdBy,
|
||||
updated_by: data.createdBy,
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`DataflowService: 테이블 관계 생성 완료 - ID: ${relationship.relationship_id}`
|
||||
);
|
||||
return relationship;
|
||||
} catch (error) {
|
||||
logger.error("DataflowService: 테이블 관계 생성 실패", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 회사별 테이블 관계 목록 조회
|
||||
*/
|
||||
async getTableRelationships(companyCode: string) {
|
||||
try {
|
||||
logger.info(
|
||||
`DataflowService: 테이블 관계 목록 조회 시작 - 회사코드: ${companyCode}`
|
||||
);
|
||||
|
||||
// 관리자는 모든 회사의 관계를 볼 수 있음
|
||||
const whereCondition: any = {
|
||||
is_active: "Y",
|
||||
};
|
||||
|
||||
if (companyCode !== "*") {
|
||||
whereCondition.company_code = companyCode;
|
||||
}
|
||||
|
||||
const relationships = await prisma.table_relationships.findMany({
|
||||
where: whereCondition,
|
||||
orderBy: {
|
||||
created_date: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`DataflowService: 테이블 관계 목록 조회 완료 - ${relationships.length}개`
|
||||
);
|
||||
return relationships;
|
||||
} catch (error) {
|
||||
logger.error("DataflowService: 테이블 관계 목록 조회 실패", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 테이블 관계 조회
|
||||
*/
|
||||
async getTableRelationship(relationshipId: number, companyCode: string) {
|
||||
try {
|
||||
logger.info(
|
||||
`DataflowService: 테이블 관계 조회 시작 - ID: ${relationshipId}, 회사코드: ${companyCode}`
|
||||
);
|
||||
|
||||
const whereCondition: any = {
|
||||
relationship_id: relationshipId,
|
||||
is_active: "Y",
|
||||
};
|
||||
|
||||
// 관리자가 아닌 경우 회사코드 제한
|
||||
if (companyCode !== "*") {
|
||||
whereCondition.company_code = companyCode;
|
||||
}
|
||||
|
||||
const relationship = await prisma.table_relationships.findFirst({
|
||||
where: whereCondition,
|
||||
});
|
||||
|
||||
if (relationship) {
|
||||
logger.info(
|
||||
`DataflowService: 테이블 관계 조회 완료 - ID: ${relationshipId}`
|
||||
);
|
||||
} else {
|
||||
logger.warn(
|
||||
`DataflowService: 테이블 관계를 찾을 수 없음 - ID: ${relationshipId}`
|
||||
);
|
||||
}
|
||||
|
||||
return relationship;
|
||||
} catch (error) {
|
||||
logger.error("DataflowService: 테이블 관계 조회 실패", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 테이블 관계 수정
|
||||
*/
|
||||
async updateTableRelationship(
|
||||
relationshipId: number,
|
||||
updateData: UpdateTableRelationshipData,
|
||||
companyCode: string
|
||||
) {
|
||||
try {
|
||||
logger.info(
|
||||
`DataflowService: 테이블 관계 수정 시작 - ID: ${relationshipId}`,
|
||||
updateData
|
||||
);
|
||||
|
||||
// 기존 관계 확인
|
||||
const existingRelationship = await this.getTableRelationship(
|
||||
relationshipId,
|
||||
companyCode
|
||||
);
|
||||
if (!existingRelationship) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 관계 수정
|
||||
const relationship = await prisma.table_relationships.update({
|
||||
where: {
|
||||
relationship_id: relationshipId,
|
||||
},
|
||||
data: {
|
||||
...updateData,
|
||||
updated_date: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`DataflowService: 테이블 관계 수정 완료 - ID: ${relationshipId}`
|
||||
);
|
||||
return relationship;
|
||||
} catch (error) {
|
||||
logger.error("DataflowService: 테이블 관계 수정 실패", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 테이블 관계 삭제 (소프트 삭제)
|
||||
*/
|
||||
async deleteTableRelationship(relationshipId: number, companyCode: string) {
|
||||
try {
|
||||
logger.info(
|
||||
`DataflowService: 테이블 관계 삭제 시작 - ID: ${relationshipId}, 회사코드: ${companyCode}`
|
||||
);
|
||||
|
||||
// 기존 관계 확인
|
||||
const existingRelationship = await this.getTableRelationship(
|
||||
relationshipId,
|
||||
companyCode
|
||||
);
|
||||
if (!existingRelationship) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 소프트 삭제 (is_active = 'N')
|
||||
await prisma.table_relationships.update({
|
||||
where: {
|
||||
relationship_id: relationshipId,
|
||||
},
|
||||
data: {
|
||||
is_active: "N",
|
||||
updated_date: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`DataflowService: 테이블 관계 삭제 완료 - ID: ${relationshipId}`
|
||||
);
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger.error("DataflowService: 테이블 관계 삭제 실패", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 테이블과 관련된 모든 관계 조회
|
||||
*/
|
||||
async getRelationshipsByTable(tableName: string, companyCode: string) {
|
||||
try {
|
||||
logger.info(
|
||||
`DataflowService: 테이블별 관계 조회 시작 - 테이블: ${tableName}, 회사코드: ${companyCode}`
|
||||
);
|
||||
|
||||
const whereCondition: any = {
|
||||
OR: [{ from_table_name: tableName }, { to_table_name: tableName }],
|
||||
is_active: "Y",
|
||||
};
|
||||
|
||||
// 관리자가 아닌 경우 회사코드 제한
|
||||
if (companyCode !== "*") {
|
||||
whereCondition.company_code = companyCode;
|
||||
}
|
||||
|
||||
const relationships = await prisma.table_relationships.findMany({
|
||||
where: whereCondition,
|
||||
orderBy: {
|
||||
created_date: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`DataflowService: 테이블별 관계 조회 완료 - ${relationships.length}개`
|
||||
);
|
||||
return relationships;
|
||||
} catch (error) {
|
||||
logger.error("DataflowService: 테이블별 관계 조회 실패", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 연결 타입별 관계 조회
|
||||
*/
|
||||
async getRelationshipsByConnectionType(
|
||||
connectionType: string,
|
||||
companyCode: string
|
||||
) {
|
||||
try {
|
||||
logger.info(
|
||||
`DataflowService: 연결타입별 관계 조회 시작 - 타입: ${connectionType}, 회사코드: ${companyCode}`
|
||||
);
|
||||
|
||||
const whereCondition: any = {
|
||||
connection_type: connectionType,
|
||||
is_active: "Y",
|
||||
};
|
||||
|
||||
// 관리자가 아닌 경우 회사코드 제한
|
||||
if (companyCode !== "*") {
|
||||
whereCondition.company_code = companyCode;
|
||||
}
|
||||
|
||||
const relationships = await prisma.table_relationships.findMany({
|
||||
where: whereCondition,
|
||||
orderBy: {
|
||||
created_date: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`DataflowService: 연결타입별 관계 조회 완료 - ${relationships.length}개`
|
||||
);
|
||||
return relationships;
|
||||
} catch (error) {
|
||||
logger.error("DataflowService: 연결타입별 관계 조회 실패", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 관계 통계 조회
|
||||
*/
|
||||
async getRelationshipStats(companyCode: string) {
|
||||
try {
|
||||
logger.info(
|
||||
`DataflowService: 관계 통계 조회 시작 - 회사코드: ${companyCode}`
|
||||
);
|
||||
|
||||
const whereCondition: any = {
|
||||
is_active: "Y",
|
||||
};
|
||||
|
||||
// 관리자가 아닌 경우 회사코드 제한
|
||||
if (companyCode !== "*") {
|
||||
whereCondition.company_code = companyCode;
|
||||
}
|
||||
|
||||
// 전체 관계 수
|
||||
const totalCount = await prisma.table_relationships.count({
|
||||
where: whereCondition,
|
||||
});
|
||||
|
||||
// 관계 타입별 통계
|
||||
const relationshipTypeStats = await prisma.table_relationships.groupBy({
|
||||
by: ["relationship_type"],
|
||||
where: whereCondition,
|
||||
_count: {
|
||||
relationship_id: true,
|
||||
},
|
||||
});
|
||||
|
||||
// 연결 타입별 통계
|
||||
const connectionTypeStats = await prisma.table_relationships.groupBy({
|
||||
by: ["connection_type"],
|
||||
where: whereCondition,
|
||||
_count: {
|
||||
relationship_id: true,
|
||||
},
|
||||
});
|
||||
|
||||
const stats = {
|
||||
totalCount,
|
||||
relationshipTypeStats: relationshipTypeStats.map((stat) => ({
|
||||
type: stat.relationship_type,
|
||||
count: stat._count.relationship_id,
|
||||
})),
|
||||
connectionTypeStats: connectionTypeStats.map((stat) => ({
|
||||
type: stat.connection_type,
|
||||
count: stat._count.relationship_id,
|
||||
})),
|
||||
};
|
||||
|
||||
logger.info(`DataflowService: 관계 통계 조회 완료`, stats);
|
||||
return stats;
|
||||
} catch (error) {
|
||||
logger.error("DataflowService: 관계 통계 조회 실패", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 데이터 중계 관리 ====================
|
||||
|
||||
/**
|
||||
* 데이터 관계 연결 생성
|
||||
*/
|
||||
async createDataLink(linkData: {
|
||||
relationshipId: number;
|
||||
fromTableName: string;
|
||||
fromColumnName: string;
|
||||
fromKeyValue: string;
|
||||
fromRecordId?: string;
|
||||
toTableName: string;
|
||||
toColumnName: string;
|
||||
toKeyValue: string;
|
||||
toRecordId?: string;
|
||||
connectionType: string;
|
||||
companyCode: string;
|
||||
bridgeData?: any;
|
||||
createdBy: string;
|
||||
}) {
|
||||
try {
|
||||
logger.info(
|
||||
`DataflowService: 데이터 연결 생성 시작 - 관계ID: ${linkData.relationshipId}`
|
||||
);
|
||||
|
||||
const bridge = await prisma.data_relationship_bridge.create({
|
||||
data: {
|
||||
relationship_id: linkData.relationshipId,
|
||||
from_table_name: linkData.fromTableName,
|
||||
from_column_name: linkData.fromColumnName,
|
||||
from_key_value: linkData.fromKeyValue,
|
||||
from_record_id: linkData.fromRecordId,
|
||||
to_table_name: linkData.toTableName,
|
||||
to_column_name: linkData.toColumnName,
|
||||
to_key_value: linkData.toKeyValue,
|
||||
to_record_id: linkData.toRecordId,
|
||||
connection_type: linkData.connectionType,
|
||||
company_code: linkData.companyCode,
|
||||
bridge_data: linkData.bridgeData || {},
|
||||
created_by: linkData.createdBy,
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`DataflowService: 데이터 연결 생성 완료 - Bridge ID: ${bridge.bridge_id}`
|
||||
);
|
||||
return bridge;
|
||||
} catch (error) {
|
||||
logger.error("DataflowService: 데이터 연결 생성 실패", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 관계별 연결된 데이터 조회
|
||||
*/
|
||||
async getLinkedDataByRelationship(
|
||||
relationshipId: number,
|
||||
companyCode: string
|
||||
) {
|
||||
try {
|
||||
logger.info(
|
||||
`DataflowService: 관계별 연결 데이터 조회 시작 - 관계ID: ${relationshipId}`
|
||||
);
|
||||
|
||||
const whereCondition: any = {
|
||||
relationship_id: relationshipId,
|
||||
is_active: "Y",
|
||||
};
|
||||
|
||||
// 관리자가 아닌 경우 회사코드 제한
|
||||
if (companyCode !== "*") {
|
||||
whereCondition.company_code = companyCode;
|
||||
}
|
||||
|
||||
const linkedData = await prisma.data_relationship_bridge.findMany({
|
||||
where: whereCondition,
|
||||
orderBy: { created_at: "desc" },
|
||||
include: {
|
||||
relationship: {
|
||||
select: {
|
||||
relationship_name: true,
|
||||
relationship_type: true,
|
||||
connection_type: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`DataflowService: 관계별 연결 데이터 조회 완료 - ${linkedData.length}건`
|
||||
);
|
||||
return linkedData;
|
||||
} catch (error) {
|
||||
logger.error("DataflowService: 관계별 연결 데이터 조회 실패", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 테이블의 연결된 데이터 조회
|
||||
*/
|
||||
async getLinkedDataByTable(
|
||||
tableName: string,
|
||||
keyValue?: string,
|
||||
companyCode?: string
|
||||
) {
|
||||
try {
|
||||
logger.info(
|
||||
`DataflowService: 테이블별 연결 데이터 조회 시작 - 테이블: ${tableName}`
|
||||
);
|
||||
|
||||
const whereCondition: any = {
|
||||
OR: [{ from_table_name: tableName }, { to_table_name: tableName }],
|
||||
is_active: "Y",
|
||||
};
|
||||
|
||||
// 특정 키 값으로 필터링
|
||||
if (keyValue) {
|
||||
whereCondition.OR = [
|
||||
{ from_table_name: tableName, from_key_value: keyValue },
|
||||
{ to_table_name: tableName, to_key_value: keyValue },
|
||||
];
|
||||
}
|
||||
|
||||
// 회사코드 필터링
|
||||
if (companyCode && companyCode !== "*") {
|
||||
whereCondition.company_code = companyCode;
|
||||
}
|
||||
|
||||
const linkedData = await prisma.data_relationship_bridge.findMany({
|
||||
where: whereCondition,
|
||||
orderBy: { created_at: "desc" },
|
||||
include: {
|
||||
relationship: {
|
||||
select: {
|
||||
relationship_name: true,
|
||||
relationship_type: true,
|
||||
connection_type: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`DataflowService: 테이블별 연결 데이터 조회 완료 - ${linkedData.length}건`
|
||||
);
|
||||
return linkedData;
|
||||
} catch (error) {
|
||||
logger.error("DataflowService: 테이블별 연결 데이터 조회 실패", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터 연결 수정
|
||||
*/
|
||||
async updateDataLink(
|
||||
bridgeId: number,
|
||||
updateData: {
|
||||
fromKeyValue?: string;
|
||||
fromRecordId?: string;
|
||||
toKeyValue?: string;
|
||||
toRecordId?: string;
|
||||
bridgeData?: any;
|
||||
updatedBy: string;
|
||||
},
|
||||
companyCode: string
|
||||
) {
|
||||
try {
|
||||
logger.info(
|
||||
`DataflowService: 데이터 연결 수정 시작 - Bridge ID: ${bridgeId}`
|
||||
);
|
||||
|
||||
const whereCondition: any = {
|
||||
bridge_id: bridgeId,
|
||||
is_active: "Y",
|
||||
};
|
||||
|
||||
// 관리자가 아닌 경우 회사코드 제한
|
||||
if (companyCode !== "*") {
|
||||
whereCondition.company_code = companyCode;
|
||||
}
|
||||
|
||||
const updatedBridge = await prisma.data_relationship_bridge.update({
|
||||
where: whereCondition,
|
||||
data: {
|
||||
...updateData,
|
||||
updated_at: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`DataflowService: 데이터 연결 수정 완료 - Bridge ID: ${bridgeId}`
|
||||
);
|
||||
return updatedBridge;
|
||||
} catch (error) {
|
||||
logger.error("DataflowService: 데이터 연결 수정 실패", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터 연결 삭제 (소프트 삭제)
|
||||
*/
|
||||
async deleteDataLink(
|
||||
bridgeId: number,
|
||||
companyCode: string,
|
||||
deletedBy: string
|
||||
) {
|
||||
try {
|
||||
logger.info(
|
||||
`DataflowService: 데이터 연결 삭제 시작 - Bridge ID: ${bridgeId}`
|
||||
);
|
||||
|
||||
const whereCondition: any = {
|
||||
bridge_id: bridgeId,
|
||||
is_active: "Y",
|
||||
};
|
||||
|
||||
// 관리자가 아닌 경우 회사코드 제한
|
||||
if (companyCode !== "*") {
|
||||
whereCondition.company_code = companyCode;
|
||||
}
|
||||
|
||||
await prisma.data_relationship_bridge.update({
|
||||
where: whereCondition,
|
||||
data: {
|
||||
is_active: "N",
|
||||
updated_at: new Date(),
|
||||
updated_by: deletedBy,
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`DataflowService: 데이터 연결 삭제 완료 - Bridge ID: ${bridgeId}`
|
||||
);
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger.error("DataflowService: 데이터 연결 삭제 실패", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 관계 삭제 시 연결된 모든 데이터도 삭제
|
||||
*/
|
||||
async deleteAllLinkedDataByRelationship(
|
||||
relationshipId: number,
|
||||
companyCode: string,
|
||||
deletedBy: string
|
||||
) {
|
||||
try {
|
||||
logger.info(
|
||||
`DataflowService: 관계별 모든 데이터 연결 삭제 시작 - 관계ID: ${relationshipId}`
|
||||
);
|
||||
|
||||
const whereCondition: any = {
|
||||
relationship_id: relationshipId,
|
||||
is_active: "Y",
|
||||
};
|
||||
|
||||
// 관리자가 아닌 경우 회사코드 제한
|
||||
if (companyCode !== "*") {
|
||||
whereCondition.company_code = companyCode;
|
||||
}
|
||||
|
||||
const result = await prisma.data_relationship_bridge.updateMany({
|
||||
where: whereCondition,
|
||||
data: {
|
||||
is_active: "N",
|
||||
updated_at: new Date(),
|
||||
updated_by: deletedBy,
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`DataflowService: 관계별 모든 데이터 연결 삭제 완료 - ${result.count}건`
|
||||
);
|
||||
return result.count;
|
||||
} catch (error) {
|
||||
logger.error("DataflowService: 관계별 모든 데이터 연결 삭제 실패", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user