테이블 노드 위치정보 저장 구현
This commit is contained in:
@@ -5324,14 +5324,15 @@ model data_relationship_bridge {
|
||||
|
||||
// 데이터플로우 관계도 - JSON 구조로 저장
|
||||
model dataflow_diagrams {
|
||||
diagram_id Int @id @default(autoincrement())
|
||||
diagram_name String @db.VarChar(255)
|
||||
relationships Json // 모든 관계 정보를 JSON으로 저장
|
||||
company_code String @db.VarChar(50)
|
||||
created_at DateTime? @default(now()) @db.Timestamp(6)
|
||||
updated_at DateTime? @default(now()) @updatedAt @db.Timestamp(6)
|
||||
created_by String? @db.VarChar(50)
|
||||
updated_by String? @db.VarChar(50)
|
||||
diagram_id Int @id @default(autoincrement())
|
||||
diagram_name String @db.VarChar(255)
|
||||
relationships Json // 모든 관계 정보를 JSON으로 저장
|
||||
node_positions Json? // 테이블 노드의 캔버스 위치 정보 (JSON 형태)
|
||||
company_code String @db.VarChar(50)
|
||||
created_at DateTime? @default(now()) @db.Timestamp(6)
|
||||
updated_at DateTime? @default(now()) @updatedAt @db.Timestamp(6)
|
||||
created_by String? @db.VarChar(50)
|
||||
updated_by String? @db.VarChar(50)
|
||||
|
||||
@@unique([company_code, diagram_name], map: "unique_diagram_name_per_company")
|
||||
@@index([company_code], map: "idx_dataflow_diagrams_company")
|
||||
|
||||
@@ -17,9 +17,17 @@ export const getDataflowDiagrams = async (req: Request, res: Response) => {
|
||||
const page = parseInt(req.query.page as string) || 1;
|
||||
const size = parseInt(req.query.size as string) || 20;
|
||||
const searchTerm = req.query.searchTerm as string;
|
||||
const companyCode = (req.query.companyCode as string) || req.headers["x-company-code"] as string || "*";
|
||||
const companyCode =
|
||||
(req.query.companyCode as string) ||
|
||||
(req.headers["x-company-code"] as string) ||
|
||||
"*";
|
||||
|
||||
const result = await getDataflowDiagramsService(companyCode, page, size, searchTerm);
|
||||
const result = await getDataflowDiagramsService(
|
||||
companyCode,
|
||||
page,
|
||||
size,
|
||||
searchTerm
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
@@ -41,7 +49,10 @@ export const getDataflowDiagrams = async (req: Request, res: Response) => {
|
||||
export const getDataflowDiagramById = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const diagramId = parseInt(req.params.diagramId);
|
||||
const companyCode = (req.query.companyCode as string) || req.headers["x-company-code"] as string || "*";
|
||||
const companyCode =
|
||||
(req.query.companyCode as string) ||
|
||||
(req.headers["x-company-code"] as string) ||
|
||||
"*";
|
||||
|
||||
if (isNaN(diagramId)) {
|
||||
return res.status(400).json({
|
||||
@@ -78,9 +89,24 @@ export const getDataflowDiagramById = async (req: Request, res: Response) => {
|
||||
*/
|
||||
export const createDataflowDiagram = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { diagram_name, relationships, company_code, created_by, updated_by } = req.body;
|
||||
const companyCode = company_code || (req.query.companyCode as string) || req.headers["x-company-code"] as string || "*";
|
||||
const userId = created_by || updated_by || req.headers["x-user-id"] as string || "SYSTEM";
|
||||
const {
|
||||
diagram_name,
|
||||
relationships,
|
||||
node_positions,
|
||||
company_code,
|
||||
created_by,
|
||||
updated_by,
|
||||
} = req.body;
|
||||
const companyCode =
|
||||
company_code ||
|
||||
(req.query.companyCode as string) ||
|
||||
(req.headers["x-company-code"] as string) ||
|
||||
"*";
|
||||
const userId =
|
||||
created_by ||
|
||||
updated_by ||
|
||||
(req.headers["x-user-id"] as string) ||
|
||||
"SYSTEM";
|
||||
|
||||
if (!diagram_name || !relationships) {
|
||||
return res.status(400).json({
|
||||
@@ -92,6 +118,7 @@ export const createDataflowDiagram = async (req: Request, res: Response) => {
|
||||
const newDiagram = await createDataflowDiagramService({
|
||||
diagram_name,
|
||||
relationships,
|
||||
node_positions,
|
||||
company_code: companyCode,
|
||||
created_by: userId,
|
||||
updated_by: userId,
|
||||
@@ -104,7 +131,7 @@ export const createDataflowDiagram = async (req: Request, res: Response) => {
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("관계도 생성 실패:", error);
|
||||
|
||||
|
||||
// 중복 이름 에러 처리
|
||||
if (error instanceof Error && error.message.includes("unique constraint")) {
|
||||
return res.status(409).json({
|
||||
@@ -128,8 +155,12 @@ export const updateDataflowDiagram = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const diagramId = parseInt(req.params.diagramId);
|
||||
const { updated_by } = req.body;
|
||||
const companyCode = (req.query.companyCode as string) || req.headers["x-company-code"] as string || "*";
|
||||
const userId = updated_by || req.headers["x-user-id"] as string || "SYSTEM";
|
||||
const companyCode =
|
||||
(req.query.companyCode as string) ||
|
||||
(req.headers["x-company-code"] as string) ||
|
||||
"*";
|
||||
const userId =
|
||||
updated_by || (req.headers["x-user-id"] as string) || "SYSTEM";
|
||||
|
||||
if (isNaN(diagramId)) {
|
||||
return res.status(400).json({
|
||||
@@ -143,7 +174,11 @@ export const updateDataflowDiagram = async (req: Request, res: Response) => {
|
||||
updated_by: userId,
|
||||
};
|
||||
|
||||
const updatedDiagram = await updateDataflowDiagramService(diagramId, updateData, companyCode);
|
||||
const updatedDiagram = await updateDataflowDiagramService(
|
||||
diagramId,
|
||||
updateData,
|
||||
companyCode
|
||||
);
|
||||
|
||||
if (!updatedDiagram) {
|
||||
return res.status(404).json({
|
||||
@@ -173,7 +208,10 @@ export const updateDataflowDiagram = async (req: Request, res: Response) => {
|
||||
export const deleteDataflowDiagram = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const diagramId = parseInt(req.params.diagramId);
|
||||
const companyCode = (req.query.companyCode as string) || req.headers["x-company-code"] as string || "*";
|
||||
const companyCode =
|
||||
(req.query.companyCode as string) ||
|
||||
(req.headers["x-company-code"] as string) ||
|
||||
"*";
|
||||
|
||||
if (isNaN(diagramId)) {
|
||||
return res.status(400).json({
|
||||
@@ -211,9 +249,18 @@ export const deleteDataflowDiagram = async (req: Request, res: Response) => {
|
||||
export const copyDataflowDiagram = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const diagramId = parseInt(req.params.diagramId);
|
||||
const { new_name, companyCode: bodyCompanyCode, userId: bodyUserId } = req.body;
|
||||
const companyCode = bodyCompanyCode || (req.query.companyCode as string) || req.headers["x-company-code"] as string || "*";
|
||||
const userId = bodyUserId || req.headers["x-user-id"] as string || "SYSTEM";
|
||||
const {
|
||||
new_name,
|
||||
companyCode: bodyCompanyCode,
|
||||
userId: bodyUserId,
|
||||
} = req.body;
|
||||
const companyCode =
|
||||
bodyCompanyCode ||
|
||||
(req.query.companyCode as string) ||
|
||||
(req.headers["x-company-code"] as string) ||
|
||||
"*";
|
||||
const userId =
|
||||
bodyUserId || (req.headers["x-user-id"] as string) || "SYSTEM";
|
||||
|
||||
if (isNaN(diagramId)) {
|
||||
return res.status(400).json({
|
||||
@@ -222,7 +269,12 @@ export const copyDataflowDiagram = async (req: Request, res: Response) => {
|
||||
});
|
||||
}
|
||||
|
||||
const copiedDiagram = await copyDataflowDiagramService(diagramId, companyCode, new_name, userId);
|
||||
const copiedDiagram = await copyDataflowDiagramService(
|
||||
diagramId,
|
||||
companyCode,
|
||||
new_name,
|
||||
userId
|
||||
);
|
||||
|
||||
if (!copiedDiagram) {
|
||||
return res.status(404).json({
|
||||
|
||||
@@ -7,6 +7,7 @@ const prisma = new PrismaClient();
|
||||
interface CreateDataflowDiagramData {
|
||||
diagram_name: string;
|
||||
relationships: any; // JSON 데이터
|
||||
node_positions?: any; // JSON 데이터 (노드 위치 정보)
|
||||
company_code: string;
|
||||
created_by: string;
|
||||
updated_by: string;
|
||||
@@ -15,6 +16,7 @@ interface CreateDataflowDiagramData {
|
||||
interface UpdateDataflowDiagramData {
|
||||
diagram_name?: string;
|
||||
relationships?: any; // JSON 데이터
|
||||
node_positions?: any; // JSON 데이터 (노드 위치 정보)
|
||||
updated_by: string;
|
||||
}
|
||||
|
||||
@@ -116,6 +118,7 @@ export const createDataflowDiagram = async (
|
||||
data: {
|
||||
diagram_name: data.diagram_name,
|
||||
relationships: data.relationships,
|
||||
node_positions: data.node_positions || null,
|
||||
company_code: data.company_code,
|
||||
created_by: data.created_by,
|
||||
updated_by: data.updated_by,
|
||||
@@ -164,6 +167,9 @@ export const updateDataflowDiagram = async (
|
||||
data: {
|
||||
...(data.diagram_name && { diagram_name: data.diagram_name }),
|
||||
...(data.relationships && { relationships: data.relationships }),
|
||||
...(data.node_positions !== undefined && {
|
||||
node_positions: data.node_positions,
|
||||
}),
|
||||
updated_by: data.updated_by,
|
||||
updated_at: new Date(),
|
||||
},
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
DataFlowDiagram,
|
||||
JsonRelationship,
|
||||
CreateDiagramRequest,
|
||||
NodePositions,
|
||||
} from "@/lib/api/dataflow";
|
||||
import SaveDiagramModal from "./SaveDiagramModal";
|
||||
import { useAuth } from "@/hooks/useAuth";
|
||||
@@ -313,10 +314,16 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
||||
|
||||
console.log("🔌 연결된 컬럼 정보:", connectedColumnsInfo);
|
||||
|
||||
// 테이블을 노드로 변환 (자동 레이아웃)
|
||||
// 저장된 노드 위치 정보 가져오기
|
||||
const savedNodePositions = jsonDiagram.node_positions || {};
|
||||
console.log("📍 저장된 노드 위치:", savedNodePositions);
|
||||
|
||||
// 테이블을 노드로 변환 (저장된 위치 우선 사용, 없으면 자동 레이아웃)
|
||||
const tableNodes = tableDefinitions.map((table, index) => {
|
||||
const x = (index % 3) * 400 + 100; // 3열 배치
|
||||
const y = Math.floor(index / 3) * 300 + 100;
|
||||
// 저장된 위치가 있으면 사용, 없으면 자동 배치
|
||||
const savedPosition = savedNodePositions[table.tableName];
|
||||
const x = savedPosition ? savedPosition.x : (index % 3) * 400 + 100; // 3열 배치
|
||||
const y = savedPosition ? savedPosition.y : Math.floor(index / 3) * 300 + 100;
|
||||
|
||||
return {
|
||||
id: `table-${table.tableName}`,
|
||||
@@ -782,6 +789,17 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
||||
new Set([...tempRelationships.map((rel) => rel.fromTable), ...tempRelationships.map((rel) => rel.toTable)]),
|
||||
).sort();
|
||||
|
||||
// 현재 노드 위치 추출
|
||||
const nodePositions: NodePositions = {};
|
||||
nodes.forEach((node) => {
|
||||
if (node.data?.table?.tableName) {
|
||||
nodePositions[node.data.table.tableName] = {
|
||||
x: node.position.x,
|
||||
y: node.position.y,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// 저장 요청 데이터 생성
|
||||
const createRequest: CreateDiagramRequest = {
|
||||
diagram_name: diagramName,
|
||||
@@ -789,6 +807,7 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
||||
relationships: tempRelationships,
|
||||
tables: connectedTables,
|
||||
},
|
||||
node_positions: nodePositions,
|
||||
};
|
||||
|
||||
let savedDiagram;
|
||||
|
||||
@@ -115,6 +115,16 @@ export interface DataFlowDiagramsResponse {
|
||||
hasPrev: boolean;
|
||||
}
|
||||
|
||||
// 노드 위치 정보 타입
|
||||
export interface NodePosition {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export interface NodePositions {
|
||||
[tableName: string]: NodePosition;
|
||||
}
|
||||
|
||||
// 새로운 JSON 기반 타입들
|
||||
export interface JsonDataFlowDiagram {
|
||||
diagram_id: number;
|
||||
@@ -123,6 +133,7 @@ export interface JsonDataFlowDiagram {
|
||||
relationships: JsonRelationship[];
|
||||
tables: string[];
|
||||
};
|
||||
node_positions?: NodePositions;
|
||||
company_code: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
@@ -147,6 +158,7 @@ export interface CreateDiagramRequest {
|
||||
relationships: JsonRelationship[];
|
||||
tables: string[];
|
||||
};
|
||||
node_positions?: NodePositions;
|
||||
}
|
||||
|
||||
export interface JsonDataFlowDiagramsResponse {
|
||||
|
||||
Reference in New Issue
Block a user