제어관리 회사코드 저장 안되는 문제 수정

This commit is contained in:
kjs
2025-12-11 10:41:28 +09:00
parent 088596480f
commit f272f0c4c7
6 changed files with 962 additions and 751 deletions

View File

@@ -218,46 +218,62 @@ router.delete("/:flowId", async (req: Request, res: Response) => {
* 플로우 실행 * 플로우 실행
* POST /api/dataflow/node-flows/:flowId/execute * POST /api/dataflow/node-flows/:flowId/execute
*/ */
router.post("/:flowId/execute", authenticateToken, async (req: AuthenticatedRequest, res: Response) => { router.post(
try { "/:flowId/execute",
const { flowId } = req.params; authenticateToken,
const contextData = req.body; async (req: AuthenticatedRequest, res: Response) => {
try {
const { flowId } = req.params;
const contextData = req.body;
logger.info(`🚀 플로우 실행 요청: flowId=${flowId}`, { logger.info(`🚀 플로우 실행 요청: flowId=${flowId}`, {
contextDataKeys: Object.keys(contextData), contextDataKeys: Object.keys(contextData),
userId: req.user?.userId, userId: req.user?.userId,
companyCode: req.user?.companyCode, companyCode: req.user?.companyCode,
}); });
// 사용자 정보를 contextData에 추가 // 🔍 디버깅: req.user 전체 확인
const enrichedContextData = { logger.info(`🔍 req.user 전체 정보:`, {
...contextData, user: req.user,
userId: req.user?.userId, hasUser: !!req.user,
userName: req.user?.userName, });
companyCode: req.user?.companyCode,
};
// 플로우 실행 // 사용자 정보를 contextData에 추가
const result = await NodeFlowExecutionService.executeFlow( const enrichedContextData = {
parseInt(flowId, 10), ...contextData,
enrichedContextData userId: req.user?.userId,
); userName: req.user?.userName,
companyCode: req.user?.companyCode,
};
return res.json({ // 🔍 디버깅: enrichedContextData 확인
success: result.success, logger.info(`🔍 enrichedContextData:`, {
message: result.message, userId: enrichedContextData.userId,
data: result, companyCode: enrichedContextData.companyCode,
}); });
} catch (error) {
logger.error("플로우 실행 실패:", error); // 플로우 실행
return res.status(500).json({ const result = await NodeFlowExecutionService.executeFlow(
success: false, parseInt(flowId, 10),
message: enrichedContextData
error instanceof Error );
? error.message
: "플로우 실행 중 오류가 발생했습니다.", return res.json({
}); success: result.success,
message: result.message,
data: result,
});
} catch (error) {
logger.error("플로우 실행 실패:", error);
return res.status(500).json({
success: false,
message:
error instanceof Error
? error.message
: "플로우 실행 중 오류가 발생했습니다.",
});
}
} }
}); );
export default router; export default router;

View File

@@ -754,12 +754,19 @@ export class DynamicFormService {
// 🎯 제어관리 실행 (새로 추가) // 🎯 제어관리 실행 (새로 추가)
try { try {
// savedData 또는 insertedRecord에서 company_code 추출
const recordCompanyCode =
(insertedRecord as Record<string, any>)?.company_code ||
dataToInsert.company_code ||
"*";
await this.executeDataflowControlIfConfigured( await this.executeDataflowControlIfConfigured(
screenId, screenId,
tableName, tableName,
insertedRecord as Record<string, any>, insertedRecord as Record<string, any>,
"insert", "insert",
created_by || "system" created_by || "system",
recordCompanyCode
); );
} catch (controlError) { } catch (controlError) {
console.error("⚠️ 제어관리 실행 오류:", controlError); console.error("⚠️ 제어관리 실행 오류:", controlError);
@@ -1107,12 +1114,19 @@ export class DynamicFormService {
// 🎯 제어관리 실행 (UPDATE 트리거) // 🎯 제어관리 실행 (UPDATE 트리거)
try { try {
// updatedRecord에서 company_code 추출
const recordCompanyCode =
(updatedRecord as Record<string, any>)?.company_code ||
company_code ||
"*";
await this.executeDataflowControlIfConfigured( await this.executeDataflowControlIfConfigured(
0, // UPDATE는 screenId를 알 수 없으므로 0으로 설정 (추후 개선 필요) 0, // UPDATE는 screenId를 알 수 없으므로 0으로 설정 (추후 개선 필요)
tableName, tableName,
updatedRecord as Record<string, any>, updatedRecord as Record<string, any>,
"update", "update",
updated_by || "system" updated_by || "system",
recordCompanyCode
); );
} catch (controlError) { } catch (controlError) {
console.error("⚠️ 제어관리 실행 오류:", controlError); console.error("⚠️ 제어관리 실행 오류:", controlError);
@@ -1251,12 +1265,17 @@ export class DynamicFormService {
try { try {
if (result && Array.isArray(result) && result.length > 0) { if (result && Array.isArray(result) && result.length > 0) {
const deletedRecord = result[0] as Record<string, any>; const deletedRecord = result[0] as Record<string, any>;
// deletedRecord에서 company_code 추출
const recordCompanyCode =
deletedRecord?.company_code || companyCode || "*";
await this.executeDataflowControlIfConfigured( await this.executeDataflowControlIfConfigured(
0, // DELETE는 screenId를 알 수 없으므로 0으로 설정 (추후 개선 필요) 0, // DELETE는 screenId를 알 수 없으므로 0으로 설정 (추후 개선 필요)
tableName, tableName,
deletedRecord, deletedRecord,
"delete", "delete",
userId || "system" userId || "system",
recordCompanyCode
); );
} }
} catch (controlError) { } catch (controlError) {
@@ -1562,7 +1581,8 @@ export class DynamicFormService {
tableName: string, tableName: string,
savedData: Record<string, any>, savedData: Record<string, any>,
triggerType: "insert" | "update" | "delete", triggerType: "insert" | "update" | "delete",
userId: string = "system" userId: string = "system",
companyCode: string = "*"
): Promise<void> { ): Promise<void> {
try { try {
console.log(`🎯 제어관리 설정 확인 중... (screenId: ${screenId})`); console.log(`🎯 제어관리 설정 확인 중... (screenId: ${screenId})`);
@@ -1636,6 +1656,7 @@ export class DynamicFormService {
buttonId: "save-button", buttonId: "save-button",
screenId: screenId, screenId: screenId,
userId: userId, userId: userId,
companyCode: companyCode,
formData: savedData, formData: savedData,
} }
); );

View File

@@ -117,6 +117,18 @@ export class NodeFlowExecutionService {
try { try {
logger.info(`🚀 플로우 실행 시작: flowId=${flowId}`); logger.info(`🚀 플로우 실행 시작: flowId=${flowId}`);
// 🔍 디버깅: contextData 상세 로그
logger.info(`🔍 contextData 상세:`, {
directCompanyCode: contextData.companyCode,
nestedCompanyCode: contextData.context?.companyCode,
directUserId: contextData.userId,
nestedUserId: contextData.context?.userId,
contextKeys: Object.keys(contextData),
nestedContextKeys: contextData.context
? Object.keys(contextData.context)
: "no nested context",
});
// 1. 플로우 데이터 조회 // 1. 플로우 데이터 조회
const flow = await queryOne<{ const flow = await queryOne<{
flow_id: number; flow_id: number;
@@ -979,12 +991,25 @@ export class NodeFlowExecutionService {
const userId = context.buttonContext?.userId; const userId = context.buttonContext?.userId;
const companyCode = context.buttonContext?.companyCode; const companyCode = context.buttonContext?.companyCode;
// 🔍 디버깅: 자동 추가 조건 확인
console.log(` 🔍 INSERT 자동 추가 조건 확인:`, {
hasWriterMapping,
hasCompanyCodeMapping,
userId,
companyCode,
buttonContext: context.buttonContext,
});
// writer 자동 추가 (매핑에 없고, 컨텍스트에 userId가 있는 경우) // writer 자동 추가 (매핑에 없고, 컨텍스트에 userId가 있는 경우)
if (!hasWriterMapping && userId) { if (!hasWriterMapping && userId) {
fields.push("writer"); fields.push("writer");
values.push(userId); values.push(userId);
insertedData.writer = userId; insertedData.writer = userId;
console.log(` 🔧 자동 추가: writer = ${userId}`); console.log(` 🔧 자동 추가: writer = ${userId}`);
} else {
console.log(
` ⚠️ writer 자동 추가 스킵: hasWriterMapping=${hasWriterMapping}, userId=${userId}`
);
} }
// company_code 자동 추가 (매핑에 없고, 컨텍스트에 companyCode가 있는 경우) // company_code 자동 추가 (매핑에 없고, 컨텍스트에 companyCode가 있는 경우)
@@ -993,6 +1018,10 @@ export class NodeFlowExecutionService {
values.push(companyCode); values.push(companyCode);
insertedData.company_code = companyCode; insertedData.company_code = companyCode;
console.log(` 🔧 자동 추가: company_code = ${companyCode}`); console.log(` 🔧 자동 추가: company_code = ${companyCode}`);
} else {
console.log(
` ⚠️ company_code 자동 추가 스킵: hasCompanyCodeMapping=${hasCompanyCodeMapping}, companyCode=${companyCode}`
);
} }
const sql = ` const sql = `
@@ -2251,6 +2280,34 @@ export class NodeFlowExecutionService {
values.push(value); values.push(value);
}); });
// 🆕 writer와 company_code 자동 추가 (필드 매핑에 없는 경우)
const hasWriterMapping = fieldMappings.some(
(m: any) => m.targetField === "writer"
);
const hasCompanyCodeMapping = fieldMappings.some(
(m: any) => m.targetField === "company_code"
);
// 컨텍스트에서 사용자 정보 추출
const userId = context.buttonContext?.userId;
const companyCode = context.buttonContext?.companyCode;
// writer 자동 추가 (매핑에 없고, 컨텍스트에 userId가 있는 경우)
if (!hasWriterMapping && userId) {
columns.push("writer");
values.push(userId);
logger.info(` 🔧 UPSERT INSERT - 자동 추가: writer = ${userId}`);
}
// company_code 자동 추가 (매핑에 없고, 컨텍스트에 companyCode가 있는 경우)
if (!hasCompanyCodeMapping && companyCode && companyCode !== "*") {
columns.push("company_code");
values.push(companyCode);
logger.info(
` 🔧 UPSERT INSERT - 자동 추가: company_code = ${companyCode}`
);
}
const placeholders = values.map((_, i) => `$${i + 1}`).join(", "); const placeholders = values.map((_, i) => `$${i + 1}`).join(", ");
const insertSql = ` const insertSql = `
INSERT INTO ${targetTable} (${columns.join(", ")}) INSERT INTO ${targetTable} (${columns.join(", ")})

View File

@@ -95,11 +95,11 @@ export interface RightPanelConfig {
actionButtons?: ActionButtonConfig[]; // 복수 액션 버튼 배열 actionButtons?: ActionButtonConfig[]; // 복수 액션 버튼 배열
primaryKeyColumn?: string; // 기본키 컬럼명 (수정/삭제용, 기본: id) primaryKeyColumn?: string; // 기본키 컬럼명 (수정/삭제용, 기본: id)
emptyMessage?: string; // 데이터 없을 때 메시지 emptyMessage?: string; // 데이터 없을 때 메시지
/** /**
* 추가 조인 테이블 설정 * 추가 조인 테이블 설정
* 메인 테이블에 다른 테이블을 JOIN하여 추가 정보를 함께 표시합니다. * 메인 테이블에 다른 테이블을 JOIN하여 추가 정보를 함께 표시합니다.
* *
* 사용 예시: * 사용 예시:
* - 메인 테이블: user_dept (부서-사용자 관계) * - 메인 테이블: user_dept (부서-사용자 관계)
* - 조인 테이블: user_info (사용자 개인정보) * - 조인 테이블: user_info (사용자 개인정보)
@@ -109,19 +109,28 @@ export interface RightPanelConfig {
} }
/** /**
* 조인 설정 * 조인 키 설정 (복합키 지원)
*/ */
export interface JoinConfig { export interface JoinKey {
leftColumn: string; // 좌측 테이블의 조인 컬럼 leftColumn: string; // 좌측 테이블의 조인 컬럼
rightColumn: string; // 우측 테이블의 조인 컬럼 rightColumn: string; // 우측 테이블의 조인 컬럼
} }
/**
* 조인 설정
*/
export interface JoinConfig {
leftColumn?: string; // 좌측 테이블의 조인 컬럼 (단일키 - 하위 호환성)
rightColumn?: string; // 우측 테이블의 조인 컬럼 (단일키 - 하위 호환성)
keys?: JoinKey[]; // 복합키 지원 (여러 컬럼으로 조인)
}
/** /**
* 추가 조인 테이블 설정 * 추가 조인 테이블 설정
* 우측 패널의 메인 테이블에 다른 테이블을 JOIN하여 추가 컬럼을 가져옵니다. * 우측 패널의 메인 테이블에 다른 테이블을 JOIN하여 추가 컬럼을 가져옵니다.
* *
* 예시: user_dept (메인) + user_info (조인) → 부서관계 + 개인정보 함께 표시 * 예시: user_dept (메인) + user_info (조인) → 부서관계 + 개인정보 함께 표시
* *
* - joinTable: 조인할 테이블명 (예: user_info) * - joinTable: 조인할 테이블명 (예: user_info)
* - joinType: 조인 방식 (LEFT JOIN 권장) * - joinType: 조인 방식 (LEFT JOIN 권장)
* - mainColumn: 메인 테이블의 조인 컬럼 (예: user_id) * - mainColumn: 메인 테이블의 조인 컬럼 (예: user_id)