Compare commits

..

No commits in common. "36132bf07c3f1f277b1fa9f04027f94181916aed" and "b77cc47791dc1311e223f2d1cb54b51d16ff65fe" have entirely different histories.

7 changed files with 33 additions and 151 deletions

View File

@ -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) {

View File

@ -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,

View File

@ -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 {

View File

@ -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);
// 복호화 실패 시 원본 사용 (또는 에러 처리) // 복호화 실패 시 원본 사용 (또는 에러 처리)

View File

@ -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);
}
} }

View File

@ -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,

View File

@ -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;