Compare commits
No commits in common. "36132bf07c3f1f277b1fa9f04027f94181916aed" and "b77cc47791dc1311e223f2d1cb54b51d16ff65fe" have entirely different histories.
36132bf07c
...
b77cc47791
|
|
@ -4,7 +4,6 @@
|
||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { BatchService } from "../services/batchService";
|
import { BatchService } from "../services/batchService";
|
||||||
import { BatchSchedulerService } from "../services/batchSchedulerService";
|
import { BatchSchedulerService } from "../services/batchSchedulerService";
|
||||||
import { BatchExternalDbService } from "../services/batchExternalDbService";
|
|
||||||
import {
|
import {
|
||||||
BatchConfigFilter,
|
BatchConfigFilter,
|
||||||
CreateBatchConfigRequest,
|
CreateBatchConfigRequest,
|
||||||
|
|
@ -64,7 +63,7 @@ export class BatchController {
|
||||||
res: Response
|
res: Response
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const result = await BatchExternalDbService.getAvailableConnections();
|
const result = await BatchService.getAvailableConnections();
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
res.json(result);
|
res.json(result);
|
||||||
|
|
@ -100,8 +99,8 @@ export class BatchController {
|
||||||
}
|
}
|
||||||
|
|
||||||
const connectionId = type === "external" ? Number(id) : undefined;
|
const connectionId = type === "external" ? Number(id) : undefined;
|
||||||
const result = await BatchService.getTables(
|
const result = await BatchService.getTablesFromConnection(
|
||||||
type as "internal" | "external",
|
type,
|
||||||
connectionId
|
connectionId
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -143,10 +142,10 @@ export class BatchController {
|
||||||
}
|
}
|
||||||
|
|
||||||
const connectionId = type === "external" ? Number(id) : undefined;
|
const connectionId = type === "external" ? Number(id) : undefined;
|
||||||
const result = await BatchService.getColumns(
|
const result = await BatchService.getTableColumns(
|
||||||
tableName,
|
type,
|
||||||
type as "internal" | "external",
|
connectionId,
|
||||||
connectionId
|
tableName
|
||||||
);
|
);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
|
|
|
||||||
|
|
@ -331,11 +331,8 @@ export class BatchManagementController {
|
||||||
const duration = endTime.getTime() - startTime.getTime();
|
const duration = endTime.getTime() - startTime.getTime();
|
||||||
|
|
||||||
// executionLog가 정의되어 있는지 확인
|
// executionLog가 정의되어 있는지 확인
|
||||||
if (typeof executionLog !== "undefined" && executionLog) {
|
if (typeof executionLog !== "undefined") {
|
||||||
const { BatchExecutionLogService } = await import(
|
await BatchService.updateExecutionLog(executionLog.id, {
|
||||||
"../services/batchExecutionLogService"
|
|
||||||
);
|
|
||||||
await BatchExecutionLogService.updateExecutionLog(executionLog.id, {
|
|
||||||
execution_status: "FAILED",
|
execution_status: "FAILED",
|
||||||
end_time: endTime,
|
end_time: endTime,
|
||||||
duration_ms: duration,
|
duration_ms: duration,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { getPool } from "../database/db";
|
import { getPool } from "../database/db";
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import { AuthenticatedRequest } from "../types/auth";
|
|
||||||
|
|
||||||
const pool = getPool();
|
const pool = getPool();
|
||||||
|
|
||||||
|
|
@ -17,7 +16,7 @@ const pool = getPool();
|
||||||
* 화면 임베딩 목록 조회
|
* 화면 임베딩 목록 조회
|
||||||
* GET /api/screen-embedding?parentScreenId=1
|
* GET /api/screen-embedding?parentScreenId=1
|
||||||
*/
|
*/
|
||||||
export async function getScreenEmbeddings(req: AuthenticatedRequest, res: Response) {
|
export async function getScreenEmbeddings(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
const { parentScreenId } = req.query;
|
const { parentScreenId } = req.query;
|
||||||
const companyCode = req.user!.companyCode;
|
const companyCode = req.user!.companyCode;
|
||||||
|
|
@ -68,7 +67,7 @@ export async function getScreenEmbeddings(req: AuthenticatedRequest, res: Respon
|
||||||
* 화면 임베딩 상세 조회
|
* 화면 임베딩 상세 조회
|
||||||
* GET /api/screen-embedding/:id
|
* GET /api/screen-embedding/:id
|
||||||
*/
|
*/
|
||||||
export async function getScreenEmbeddingById(req: AuthenticatedRequest, res: Response) {
|
export async function getScreenEmbeddingById(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const companyCode = req.user!.companyCode;
|
const companyCode = req.user!.companyCode;
|
||||||
|
|
@ -114,7 +113,7 @@ export async function getScreenEmbeddingById(req: AuthenticatedRequest, res: Res
|
||||||
* 화면 임베딩 생성
|
* 화면 임베딩 생성
|
||||||
* POST /api/screen-embedding
|
* POST /api/screen-embedding
|
||||||
*/
|
*/
|
||||||
export async function createScreenEmbedding(req: AuthenticatedRequest, res: Response) {
|
export async function createScreenEmbedding(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
parentScreenId,
|
parentScreenId,
|
||||||
|
|
@ -185,7 +184,7 @@ export async function createScreenEmbedding(req: AuthenticatedRequest, res: Resp
|
||||||
* 화면 임베딩 수정
|
* 화면 임베딩 수정
|
||||||
* PUT /api/screen-embedding/:id
|
* PUT /api/screen-embedding/:id
|
||||||
*/
|
*/
|
||||||
export async function updateScreenEmbedding(req: AuthenticatedRequest, res: Response) {
|
export async function updateScreenEmbedding(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const { position, mode, config } = req.body;
|
const { position, mode, config } = req.body;
|
||||||
|
|
@ -258,7 +257,7 @@ export async function updateScreenEmbedding(req: AuthenticatedRequest, res: Resp
|
||||||
* 화면 임베딩 삭제
|
* 화면 임베딩 삭제
|
||||||
* DELETE /api/screen-embedding/:id
|
* DELETE /api/screen-embedding/:id
|
||||||
*/
|
*/
|
||||||
export async function deleteScreenEmbedding(req: AuthenticatedRequest, res: Response) {
|
export async function deleteScreenEmbedding(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const companyCode = req.user!.companyCode;
|
const companyCode = req.user!.companyCode;
|
||||||
|
|
@ -302,7 +301,7 @@ export async function deleteScreenEmbedding(req: AuthenticatedRequest, res: Resp
|
||||||
* 데이터 전달 설정 조회
|
* 데이터 전달 설정 조회
|
||||||
* GET /api/screen-data-transfer?sourceScreenId=1&targetScreenId=2
|
* GET /api/screen-data-transfer?sourceScreenId=1&targetScreenId=2
|
||||||
*/
|
*/
|
||||||
export async function getScreenDataTransfer(req: AuthenticatedRequest, res: Response) {
|
export async function getScreenDataTransfer(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
const { sourceScreenId, targetScreenId } = req.query;
|
const { sourceScreenId, targetScreenId } = req.query;
|
||||||
const companyCode = req.user!.companyCode;
|
const companyCode = req.user!.companyCode;
|
||||||
|
|
@ -364,7 +363,7 @@ export async function getScreenDataTransfer(req: AuthenticatedRequest, res: Resp
|
||||||
* 데이터 전달 설정 생성
|
* 데이터 전달 설정 생성
|
||||||
* POST /api/screen-data-transfer
|
* POST /api/screen-data-transfer
|
||||||
*/
|
*/
|
||||||
export async function createScreenDataTransfer(req: AuthenticatedRequest, res: Response) {
|
export async function createScreenDataTransfer(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
sourceScreenId,
|
sourceScreenId,
|
||||||
|
|
@ -437,7 +436,7 @@ export async function createScreenDataTransfer(req: AuthenticatedRequest, res: R
|
||||||
* 데이터 전달 설정 수정
|
* 데이터 전달 설정 수정
|
||||||
* PUT /api/screen-data-transfer/:id
|
* PUT /api/screen-data-transfer/:id
|
||||||
*/
|
*/
|
||||||
export async function updateScreenDataTransfer(req: AuthenticatedRequest, res: Response) {
|
export async function updateScreenDataTransfer(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const { dataReceivers, buttonConfig } = req.body;
|
const { dataReceivers, buttonConfig } = req.body;
|
||||||
|
|
@ -505,7 +504,7 @@ export async function updateScreenDataTransfer(req: AuthenticatedRequest, res: R
|
||||||
* 데이터 전달 설정 삭제
|
* 데이터 전달 설정 삭제
|
||||||
* DELETE /api/screen-data-transfer/:id
|
* DELETE /api/screen-data-transfer/:id
|
||||||
*/
|
*/
|
||||||
export async function deleteScreenDataTransfer(req: AuthenticatedRequest, res: Response) {
|
export async function deleteScreenDataTransfer(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const companyCode = req.user!.companyCode;
|
const companyCode = req.user!.companyCode;
|
||||||
|
|
@ -549,7 +548,7 @@ export async function deleteScreenDataTransfer(req: AuthenticatedRequest, res: R
|
||||||
* 분할 패널 설정 조회
|
* 분할 패널 설정 조회
|
||||||
* GET /api/screen-split-panel/:screenId
|
* GET /api/screen-split-panel/:screenId
|
||||||
*/
|
*/
|
||||||
export async function getScreenSplitPanel(req: AuthenticatedRequest, res: Response) {
|
export async function getScreenSplitPanel(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
const { screenId } = req.params;
|
const { screenId } = req.params;
|
||||||
const companyCode = req.user!.companyCode;
|
const companyCode = req.user!.companyCode;
|
||||||
|
|
@ -656,7 +655,7 @@ export async function getScreenSplitPanel(req: AuthenticatedRequest, res: Respon
|
||||||
* 분할 패널 설정 생성
|
* 분할 패널 설정 생성
|
||||||
* POST /api/screen-split-panel
|
* POST /api/screen-split-panel
|
||||||
*/
|
*/
|
||||||
export async function createScreenSplitPanel(req: AuthenticatedRequest, res: Response) {
|
export async function createScreenSplitPanel(req: Request, res: Response) {
|
||||||
const client = await pool.connect();
|
const client = await pool.connect();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -793,7 +792,7 @@ export async function createScreenSplitPanel(req: AuthenticatedRequest, res: Res
|
||||||
* 분할 패널 설정 수정
|
* 분할 패널 설정 수정
|
||||||
* PUT /api/screen-split-panel/:id
|
* PUT /api/screen-split-panel/:id
|
||||||
*/
|
*/
|
||||||
export async function updateScreenSplitPanel(req: AuthenticatedRequest, res: Response) {
|
export async function updateScreenSplitPanel(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const { layoutConfig } = req.body;
|
const { layoutConfig } = req.body;
|
||||||
|
|
@ -846,7 +845,7 @@ export async function updateScreenSplitPanel(req: AuthenticatedRequest, res: Res
|
||||||
* 분할 패널 설정 삭제
|
* 분할 패널 설정 삭제
|
||||||
* DELETE /api/screen-split-panel/:id
|
* DELETE /api/screen-split-panel/:id
|
||||||
*/
|
*/
|
||||||
export async function deleteScreenSplitPanel(req: AuthenticatedRequest, res: Response) {
|
export async function deleteScreenSplitPanel(req: Request, res: Response) {
|
||||||
const client = await pool.connect();
|
const client = await pool.connect();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,8 @@ export class BatchExternalDbService {
|
||||||
// 비밀번호 복호화
|
// 비밀번호 복호화
|
||||||
if (connection.password) {
|
if (connection.password) {
|
||||||
try {
|
try {
|
||||||
connection.password = PasswordEncryption.decrypt(connection.password);
|
const passwordEncryption = new PasswordEncryption();
|
||||||
|
connection.password = passwordEncryption.decrypt(connection.password);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("비밀번호 복호화 실패:", error);
|
console.error("비밀번호 복호화 실패:", error);
|
||||||
// 복호화 실패 시 원본 사용 (또는 에러 처리)
|
// 복호화 실패 시 원본 사용 (또는 에러 처리)
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import cron, { ScheduledTask } from "node-cron";
|
import cron from "node-cron";
|
||||||
import { BatchService } from "./batchService";
|
import { BatchService } from "./batchService";
|
||||||
import { BatchExecutionLogService } from "./batchExecutionLogService";
|
import { BatchExecutionLogService } from "./batchExecutionLogService";
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
|
|
||||||
export class BatchSchedulerService {
|
export class BatchSchedulerService {
|
||||||
private static scheduledTasks: Map<number, ScheduledTask> = new Map();
|
private static scheduledTasks: Map<number, cron.ScheduledTask> = new Map();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 모든 활성 배치의 스케줄링 초기화
|
* 모든 활성 배치의 스케줄링 초기화
|
||||||
|
|
@ -183,7 +183,7 @@ export class BatchSchedulerService {
|
||||||
// 실행 로그 업데이트 (실패)
|
// 실행 로그 업데이트 (실패)
|
||||||
if (executionLog) {
|
if (executionLog) {
|
||||||
await BatchExecutionLogService.updateExecutionLog(executionLog.id, {
|
await BatchExecutionLogService.updateExecutionLog(executionLog.id, {
|
||||||
execution_status: "FAILED",
|
execution_status: "FAILURE",
|
||||||
end_time: new Date(),
|
end_time: new Date(),
|
||||||
duration_ms: Date.now() - startTime.getTime(),
|
duration_ms: Date.now() - startTime.getTime(),
|
||||||
error_message:
|
error_message:
|
||||||
|
|
@ -404,11 +404,4 @@ export class BatchSchedulerService {
|
||||||
|
|
||||||
return { totalRecords, successRecords, failedRecords };
|
return { totalRecords, successRecords, failedRecords };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 개별 배치 작업 스케줄링 (scheduleBatch의 별칭)
|
|
||||||
*/
|
|
||||||
static async scheduleBatchConfig(config: any) {
|
|
||||||
return this.scheduleBatch(config);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import {
|
||||||
UpdateBatchConfigRequest,
|
UpdateBatchConfigRequest,
|
||||||
} from "../types/batchTypes";
|
} from "../types/batchTypes";
|
||||||
import { BatchExternalDbService } from "./batchExternalDbService";
|
import { BatchExternalDbService } from "./batchExternalDbService";
|
||||||
|
import { DbConnectionManager } from "./dbConnectionManager";
|
||||||
|
|
||||||
export class BatchService {
|
export class BatchService {
|
||||||
/**
|
/**
|
||||||
|
|
@ -474,13 +475,7 @@ export class BatchService {
|
||||||
try {
|
try {
|
||||||
if (connectionType === "internal") {
|
if (connectionType === "internal") {
|
||||||
// 내부 DB 테이블 조회
|
// 내부 DB 테이블 조회
|
||||||
const tables = await query<TableInfo>(
|
const tables = await DbConnectionManager.getInternalTables();
|
||||||
`SELECT table_name, table_type, table_schema
|
|
||||||
FROM information_schema.tables
|
|
||||||
WHERE table_schema = 'public'
|
|
||||||
AND table_type = 'BASE TABLE'
|
|
||||||
ORDER BY table_name`
|
|
||||||
);
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
data: tables,
|
data: tables,
|
||||||
|
|
@ -514,13 +509,7 @@ export class BatchService {
|
||||||
try {
|
try {
|
||||||
if (connectionType === "internal") {
|
if (connectionType === "internal") {
|
||||||
// 내부 DB 컬럼 조회
|
// 내부 DB 컬럼 조회
|
||||||
const columns = await query<ColumnInfo>(
|
const columns = await DbConnectionManager.getInternalColumns(tableName);
|
||||||
`SELECT column_name, data_type, is_nullable, column_default
|
|
||||||
FROM information_schema.columns
|
|
||||||
WHERE table_schema = 'public' AND table_name = $1
|
|
||||||
ORDER BY ordinal_position`,
|
|
||||||
[tableName]
|
|
||||||
);
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
data: columns,
|
data: columns,
|
||||||
|
|
@ -554,9 +543,7 @@ export class BatchService {
|
||||||
try {
|
try {
|
||||||
if (connectionType === "internal") {
|
if (connectionType === "internal") {
|
||||||
// 내부 DB 데이터 조회
|
// 내부 DB 데이터 조회
|
||||||
const data = await query<any>(
|
const data = await DbConnectionManager.getInternalData(tableName, 10);
|
||||||
`SELECT * FROM ${tableName} LIMIT 10`
|
|
||||||
);
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
data,
|
data,
|
||||||
|
|
|
||||||
|
|
@ -1,98 +1,4 @@
|
||||||
// 배치관리 타입 정의
|
import { ApiResponse, ColumnInfo } from './batchTypes';
|
||||||
// 작성일: 2024-12-24
|
|
||||||
|
|
||||||
// 공통 API 응답 타입
|
|
||||||
export interface ApiResponse<T = any> {
|
|
||||||
success: boolean;
|
|
||||||
data?: T;
|
|
||||||
message?: string;
|
|
||||||
error?: string;
|
|
||||||
pagination?: {
|
|
||||||
page: number;
|
|
||||||
limit: number;
|
|
||||||
total: number;
|
|
||||||
totalPages: number;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 컬럼 정보 타입
|
|
||||||
export interface ColumnInfo {
|
|
||||||
column_name: string;
|
|
||||||
data_type: string;
|
|
||||||
is_nullable?: string;
|
|
||||||
column_default?: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 테이블 정보 타입
|
|
||||||
export interface TableInfo {
|
|
||||||
table_name: string;
|
|
||||||
table_type?: string;
|
|
||||||
table_schema?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 연결 정보 타입
|
|
||||||
export interface ConnectionInfo {
|
|
||||||
type: 'internal' | 'external';
|
|
||||||
id?: number;
|
|
||||||
name: string;
|
|
||||||
db_type?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 배치 설정 필터 타입
|
|
||||||
export interface BatchConfigFilter {
|
|
||||||
page?: number;
|
|
||||||
limit?: number;
|
|
||||||
search?: string;
|
|
||||||
is_active?: string;
|
|
||||||
company_code?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 배치 매핑 타입
|
|
||||||
export interface BatchMapping {
|
|
||||||
id?: number;
|
|
||||||
batch_config_id?: number;
|
|
||||||
company_code?: string;
|
|
||||||
from_connection_type: 'internal' | 'external' | 'restapi';
|
|
||||||
from_connection_id?: number;
|
|
||||||
from_table_name: string;
|
|
||||||
from_column_name: string;
|
|
||||||
from_column_type?: string;
|
|
||||||
from_api_url?: string;
|
|
||||||
from_api_key?: string;
|
|
||||||
from_api_method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
||||||
from_api_param_type?: 'url' | 'query';
|
|
||||||
from_api_param_name?: string;
|
|
||||||
from_api_param_value?: string;
|
|
||||||
from_api_param_source?: 'static' | 'dynamic';
|
|
||||||
from_api_body?: string;
|
|
||||||
to_connection_type: 'internal' | 'external' | 'restapi';
|
|
||||||
to_connection_id?: number;
|
|
||||||
to_table_name: string;
|
|
||||||
to_column_name: string;
|
|
||||||
to_column_type?: string;
|
|
||||||
to_api_url?: string;
|
|
||||||
to_api_key?: string;
|
|
||||||
to_api_method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
||||||
to_api_body?: string;
|
|
||||||
mapping_order?: number;
|
|
||||||
created_by?: string;
|
|
||||||
created_date?: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 배치 설정 타입
|
|
||||||
export interface BatchConfig {
|
|
||||||
id?: number;
|
|
||||||
batch_name: string;
|
|
||||||
description?: string;
|
|
||||||
cron_schedule: string;
|
|
||||||
is_active: 'Y' | 'N';
|
|
||||||
company_code?: string;
|
|
||||||
created_by?: string;
|
|
||||||
created_date?: Date;
|
|
||||||
updated_by?: string;
|
|
||||||
updated_date?: Date;
|
|
||||||
batch_mappings?: BatchMapping[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BatchConnectionInfo {
|
export interface BatchConnectionInfo {
|
||||||
type: 'internal' | 'external';
|
type: 'internal' | 'external';
|
||||||
|
|
@ -121,7 +27,7 @@ export interface BatchMappingRequest {
|
||||||
from_api_param_name?: string; // API 파라미터명
|
from_api_param_name?: string; // API 파라미터명
|
||||||
from_api_param_value?: string; // API 파라미터 값 또는 템플릿
|
from_api_param_value?: string; // API 파라미터 값 또는 템플릿
|
||||||
from_api_param_source?: 'static' | 'dynamic'; // 파라미터 소스 타입
|
from_api_param_source?: 'static' | 'dynamic'; // 파라미터 소스 타입
|
||||||
// REST API Body 추가 (FROM - REST API에서 POST 요청 시 필요)
|
// 👇 REST API Body 추가 (FROM - REST API에서 POST 요청 시 필요)
|
||||||
from_api_body?: string;
|
from_api_body?: string;
|
||||||
to_connection_type: 'internal' | 'external' | 'restapi';
|
to_connection_type: 'internal' | 'external' | 'restapi';
|
||||||
to_connection_id?: number;
|
to_connection_id?: number;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue