feat: Phase 3.15 배치 서비스 Raw Query 전환 완료
4개 서비스 24개 Prisma 호출 전환 완료 배치 서비스 전환: - BatchExternalDbService (8개) - BatchExecutionLogService (7개) - BatchManagementService (5개) - BatchSchedulerService (4개) 주요 기술: - json_agg + json_build_object - 동적 WHERE 절 - 동적 UPDATE 쿼리 - PostgreSQL placeholders Phase 3 완료 문서: PHASE3.15_BATCH_SERVICES_MIGRATION.md
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// 배치 실행 로그 서비스
|
||||
// 작성일: 2024-12-24
|
||||
|
||||
import prisma from "../config/database";
|
||||
import { query, queryOne } from "../database/db";
|
||||
import {
|
||||
BatchExecutionLog,
|
||||
CreateBatchExecutionLogRequest,
|
||||
@@ -32,48 +32,65 @@ export class BatchExecutionLogService {
|
||||
const take = limit;
|
||||
|
||||
// WHERE 조건 구성
|
||||
const where: any = {};
|
||||
const whereConditions: string[] = [];
|
||||
const params: any[] = [];
|
||||
let paramIndex = 1;
|
||||
|
||||
if (batch_config_id) {
|
||||
where.batch_config_id = batch_config_id;
|
||||
whereConditions.push(`bel.batch_config_id = $${paramIndex++}`);
|
||||
params.push(batch_config_id);
|
||||
}
|
||||
|
||||
if (execution_status) {
|
||||
where.execution_status = execution_status;
|
||||
whereConditions.push(`bel.execution_status = $${paramIndex++}`);
|
||||
params.push(execution_status);
|
||||
}
|
||||
|
||||
if (start_date || end_date) {
|
||||
where.start_time = {};
|
||||
if (start_date) {
|
||||
where.start_time.gte = start_date;
|
||||
}
|
||||
if (end_date) {
|
||||
where.start_time.lte = end_date;
|
||||
}
|
||||
if (start_date) {
|
||||
whereConditions.push(`bel.start_time >= $${paramIndex++}`);
|
||||
params.push(start_date);
|
||||
}
|
||||
|
||||
if (end_date) {
|
||||
whereConditions.push(`bel.start_time <= $${paramIndex++}`);
|
||||
params.push(end_date);
|
||||
}
|
||||
|
||||
// 로그 조회
|
||||
const [logs, total] = await Promise.all([
|
||||
prisma.batch_execution_logs.findMany({
|
||||
where,
|
||||
include: {
|
||||
batch_config: {
|
||||
select: {
|
||||
id: true,
|
||||
batch_name: true,
|
||||
description: true,
|
||||
cron_schedule: true,
|
||||
is_active: true
|
||||
}
|
||||
}
|
||||
},
|
||||
orderBy: { start_time: 'desc' },
|
||||
skip,
|
||||
take
|
||||
}),
|
||||
prisma.batch_execution_logs.count({ where })
|
||||
const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(' AND ')}` : '';
|
||||
|
||||
// 로그 조회 (batch_config 정보 포함)
|
||||
const sql = `
|
||||
SELECT
|
||||
bel.*,
|
||||
json_build_object(
|
||||
'id', bc.id,
|
||||
'batch_name', bc.batch_name,
|
||||
'description', bc.description,
|
||||
'cron_schedule', bc.cron_schedule,
|
||||
'is_active', bc.is_active
|
||||
) as batch_config
|
||||
FROM batch_execution_logs bel
|
||||
LEFT JOIN batch_configs bc ON bel.batch_config_id = bc.id
|
||||
${whereClause}
|
||||
ORDER BY bel.start_time DESC
|
||||
LIMIT $${paramIndex} OFFSET $${paramIndex + 1}
|
||||
`;
|
||||
|
||||
const countSql = `
|
||||
SELECT COUNT(*) as count
|
||||
FROM batch_execution_logs bel
|
||||
${whereClause}
|
||||
`;
|
||||
|
||||
params.push(take, skip);
|
||||
|
||||
const [logs, countResult] = await Promise.all([
|
||||
query<any>(sql, params),
|
||||
query<{ count: number }>(countSql, params.slice(0, -2))
|
||||
]);
|
||||
|
||||
const total = parseInt(countResult[0]?.count?.toString() || '0', 10);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: logs as BatchExecutionLogWithConfig[],
|
||||
@@ -101,22 +118,28 @@ export class BatchExecutionLogService {
|
||||
data: CreateBatchExecutionLogRequest
|
||||
): Promise<ApiResponse<BatchExecutionLog>> {
|
||||
try {
|
||||
const log = await prisma.batch_execution_logs.create({
|
||||
data: {
|
||||
batch_config_id: data.batch_config_id,
|
||||
execution_status: data.execution_status,
|
||||
start_time: data.start_time || new Date(),
|
||||
end_time: data.end_time,
|
||||
duration_ms: data.duration_ms,
|
||||
total_records: data.total_records || 0,
|
||||
success_records: data.success_records || 0,
|
||||
failed_records: data.failed_records || 0,
|
||||
error_message: data.error_message,
|
||||
error_details: data.error_details,
|
||||
server_name: data.server_name || process.env.HOSTNAME || 'unknown',
|
||||
process_id: data.process_id || process.pid?.toString()
|
||||
}
|
||||
});
|
||||
const log = await queryOne<BatchExecutionLog>(
|
||||
`INSERT INTO batch_execution_logs (
|
||||
batch_config_id, execution_status, start_time, end_time,
|
||||
duration_ms, total_records, success_records, failed_records,
|
||||
error_message, error_details, server_name, process_id
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
||||
RETURNING *`,
|
||||
[
|
||||
data.batch_config_id,
|
||||
data.execution_status,
|
||||
data.start_time || new Date(),
|
||||
data.end_time,
|
||||
data.duration_ms,
|
||||
data.total_records || 0,
|
||||
data.success_records || 0,
|
||||
data.failed_records || 0,
|
||||
data.error_message,
|
||||
data.error_details,
|
||||
data.server_name || process.env.HOSTNAME || 'unknown',
|
||||
data.process_id || process.pid?.toString()
|
||||
]
|
||||
);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
@@ -141,19 +164,53 @@ export class BatchExecutionLogService {
|
||||
data: UpdateBatchExecutionLogRequest
|
||||
): Promise<ApiResponse<BatchExecutionLog>> {
|
||||
try {
|
||||
const log = await prisma.batch_execution_logs.update({
|
||||
where: { id },
|
||||
data: {
|
||||
execution_status: data.execution_status,
|
||||
end_time: data.end_time,
|
||||
duration_ms: data.duration_ms,
|
||||
total_records: data.total_records,
|
||||
success_records: data.success_records,
|
||||
failed_records: data.failed_records,
|
||||
error_message: data.error_message,
|
||||
error_details: data.error_details
|
||||
}
|
||||
});
|
||||
// 동적 UPDATE 쿼리 생성
|
||||
const updates: string[] = [];
|
||||
const params: any[] = [];
|
||||
let paramIndex = 1;
|
||||
|
||||
if (data.execution_status !== undefined) {
|
||||
updates.push(`execution_status = $${paramIndex++}`);
|
||||
params.push(data.execution_status);
|
||||
}
|
||||
if (data.end_time !== undefined) {
|
||||
updates.push(`end_time = $${paramIndex++}`);
|
||||
params.push(data.end_time);
|
||||
}
|
||||
if (data.duration_ms !== undefined) {
|
||||
updates.push(`duration_ms = $${paramIndex++}`);
|
||||
params.push(data.duration_ms);
|
||||
}
|
||||
if (data.total_records !== undefined) {
|
||||
updates.push(`total_records = $${paramIndex++}`);
|
||||
params.push(data.total_records);
|
||||
}
|
||||
if (data.success_records !== undefined) {
|
||||
updates.push(`success_records = $${paramIndex++}`);
|
||||
params.push(data.success_records);
|
||||
}
|
||||
if (data.failed_records !== undefined) {
|
||||
updates.push(`failed_records = $${paramIndex++}`);
|
||||
params.push(data.failed_records);
|
||||
}
|
||||
if (data.error_message !== undefined) {
|
||||
updates.push(`error_message = $${paramIndex++}`);
|
||||
params.push(data.error_message);
|
||||
}
|
||||
if (data.error_details !== undefined) {
|
||||
updates.push(`error_details = $${paramIndex++}`);
|
||||
params.push(data.error_details);
|
||||
}
|
||||
|
||||
params.push(id);
|
||||
|
||||
const log = await queryOne<BatchExecutionLog>(
|
||||
`UPDATE batch_execution_logs
|
||||
SET ${updates.join(', ')}
|
||||
WHERE id = $${paramIndex}
|
||||
RETURNING *`,
|
||||
params
|
||||
);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
@@ -175,9 +232,7 @@ export class BatchExecutionLogService {
|
||||
*/
|
||||
static async deleteExecutionLog(id: number): Promise<ApiResponse<void>> {
|
||||
try {
|
||||
await prisma.batch_execution_logs.delete({
|
||||
where: { id }
|
||||
});
|
||||
await query(`DELETE FROM batch_execution_logs WHERE id = $1`, [id]);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
@@ -200,14 +255,17 @@ export class BatchExecutionLogService {
|
||||
batchConfigId: number
|
||||
): Promise<ApiResponse<BatchExecutionLog | null>> {
|
||||
try {
|
||||
const log = await prisma.batch_execution_logs.findFirst({
|
||||
where: { batch_config_id: batchConfigId },
|
||||
orderBy: { start_time: 'desc' }
|
||||
});
|
||||
const log = await queryOne<BatchExecutionLog>(
|
||||
`SELECT * FROM batch_execution_logs
|
||||
WHERE batch_config_id = $1
|
||||
ORDER BY start_time DESC
|
||||
LIMIT 1`,
|
||||
[batchConfigId]
|
||||
);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: log as BatchExecutionLog | null
|
||||
data: log || null
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("최신 배치 실행 로그 조회 실패:", error);
|
||||
@@ -235,30 +293,37 @@ export class BatchExecutionLogService {
|
||||
total_records_processed: number;
|
||||
}>> {
|
||||
try {
|
||||
const where: any = {};
|
||||
const whereConditions: string[] = [];
|
||||
const params: any[] = [];
|
||||
let paramIndex = 1;
|
||||
|
||||
if (batchConfigId) {
|
||||
where.batch_config_id = batchConfigId;
|
||||
whereConditions.push(`batch_config_id = $${paramIndex++}`);
|
||||
params.push(batchConfigId);
|
||||
}
|
||||
|
||||
if (startDate || endDate) {
|
||||
where.start_time = {};
|
||||
if (startDate) {
|
||||
where.start_time.gte = startDate;
|
||||
}
|
||||
if (endDate) {
|
||||
where.start_time.lte = endDate;
|
||||
}
|
||||
if (startDate) {
|
||||
whereConditions.push(`start_time >= $${paramIndex++}`);
|
||||
params.push(startDate);
|
||||
}
|
||||
|
||||
if (endDate) {
|
||||
whereConditions.push(`start_time <= $${paramIndex++}`);
|
||||
params.push(endDate);
|
||||
}
|
||||
|
||||
const logs = await prisma.batch_execution_logs.findMany({
|
||||
where,
|
||||
select: {
|
||||
execution_status: true,
|
||||
duration_ms: true,
|
||||
total_records: true
|
||||
}
|
||||
});
|
||||
const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(' AND ')}` : '';
|
||||
|
||||
const logs = await query<{
|
||||
execution_status: string;
|
||||
duration_ms: number;
|
||||
total_records: number;
|
||||
}>(
|
||||
`SELECT execution_status, duration_ms, total_records
|
||||
FROM batch_execution_logs
|
||||
${whereClause}`,
|
||||
params
|
||||
);
|
||||
|
||||
const total_executions = logs.length;
|
||||
const success_count = logs.filter((log: any) => log.execution_status === 'SUCCESS').length;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// 기존 ExternalDbConnectionService와 분리하여 배치관리 시스템에 특화된 기능 제공
|
||||
// 작성일: 2024-12-24
|
||||
|
||||
import prisma from "../config/database";
|
||||
import { query, queryOne } from "../database/db";
|
||||
import { PasswordEncryption } from "../utils/passwordEncryption";
|
||||
import { DatabaseConnectorFactory } from "../database/DatabaseConnectorFactory";
|
||||
import { RestApiConnector } from "../database/RestApiConnector";
|
||||
@@ -34,16 +34,18 @@ export class BatchExternalDbService {
|
||||
});
|
||||
|
||||
// 활성화된 외부 DB 연결 조회
|
||||
const externalConnections = await prisma.external_db_connections.findMany({
|
||||
where: { is_active: 'Y' },
|
||||
select: {
|
||||
id: true,
|
||||
connection_name: true,
|
||||
db_type: true,
|
||||
description: true
|
||||
},
|
||||
orderBy: { connection_name: 'asc' }
|
||||
});
|
||||
const externalConnections = await query<{
|
||||
id: number;
|
||||
connection_name: string;
|
||||
db_type: string;
|
||||
description: string;
|
||||
}>(
|
||||
`SELECT id, connection_name, db_type, description
|
||||
FROM external_db_connections
|
||||
WHERE is_active = 'Y'
|
||||
ORDER BY connection_name ASC`,
|
||||
[]
|
||||
);
|
||||
|
||||
// 외부 DB 연결 추가
|
||||
externalConnections.forEach(conn => {
|
||||
@@ -82,13 +84,14 @@ export class BatchExternalDbService {
|
||||
|
||||
if (connectionType === 'internal') {
|
||||
// 내부 DB 테이블 조회
|
||||
const result = await prisma.$queryRaw<Array<{ table_name: string }>>`
|
||||
SELECT table_name
|
||||
const result = await query<{ table_name: string }>(
|
||||
`SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
AND table_type = 'BASE TABLE'
|
||||
ORDER BY table_name
|
||||
`;
|
||||
ORDER BY table_name`,
|
||||
[]
|
||||
);
|
||||
|
||||
tables = result.map(row => ({
|
||||
table_name: row.table_name,
|
||||
@@ -138,22 +141,23 @@ export class BatchExternalDbService {
|
||||
// 내부 DB 컬럼 조회
|
||||
console.log(`[BatchExternalDbService] 내부 DB 컬럼 조회 시작: ${tableName}`);
|
||||
|
||||
const result = await prisma.$queryRaw<Array<{
|
||||
const result = await query<{
|
||||
column_name: string;
|
||||
data_type: string;
|
||||
is_nullable: string;
|
||||
column_default: string | null
|
||||
}>>`
|
||||
SELECT
|
||||
}>(
|
||||
`SELECT
|
||||
column_name,
|
||||
data_type,
|
||||
is_nullable,
|
||||
column_default
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = ${tableName}
|
||||
ORDER BY ordinal_position
|
||||
`;
|
||||
AND table_name = $1
|
||||
ORDER BY ordinal_position`,
|
||||
[tableName]
|
||||
);
|
||||
|
||||
console.log(`[BatchExternalDbService] 내부 DB 컬럼 조회 결과:`, result);
|
||||
|
||||
@@ -198,9 +202,10 @@ export class BatchExternalDbService {
|
||||
private static async getExternalTables(connectionId: number): Promise<ApiResponse<TableInfo[]>> {
|
||||
try {
|
||||
// 연결 정보 조회
|
||||
const connection = await prisma.external_db_connections.findUnique({
|
||||
where: { id: connectionId }
|
||||
});
|
||||
const connection = await queryOne<any>(
|
||||
`SELECT * FROM external_db_connections WHERE id = $1`,
|
||||
[connectionId]
|
||||
);
|
||||
|
||||
if (!connection) {
|
||||
return {
|
||||
@@ -257,9 +262,10 @@ export class BatchExternalDbService {
|
||||
console.log(`[BatchExternalDbService] getExternalTableColumns 호출: connectionId=${connectionId}, tableName=${tableName}`);
|
||||
|
||||
// 연결 정보 조회
|
||||
const connection = await prisma.external_db_connections.findUnique({
|
||||
where: { id: connectionId }
|
||||
});
|
||||
const connection = await queryOne<any>(
|
||||
`SELECT * FROM external_db_connections WHERE id = $1`,
|
||||
[connectionId]
|
||||
);
|
||||
|
||||
if (!connection) {
|
||||
console.log(`[BatchExternalDbService] 연결 정보를 찾을 수 없음: connectionId=${connectionId}`);
|
||||
@@ -418,9 +424,10 @@ export class BatchExternalDbService {
|
||||
console.log(`[BatchExternalDbService] 외부 DB 데이터 조회: connectionId=${connectionId}, tableName=${tableName}`);
|
||||
|
||||
// 외부 DB 연결 정보 조회
|
||||
const connection = await prisma.external_db_connections.findUnique({
|
||||
where: { id: connectionId }
|
||||
});
|
||||
const connection = await queryOne<any>(
|
||||
`SELECT * FROM external_db_connections WHERE id = $1`,
|
||||
[connectionId]
|
||||
);
|
||||
|
||||
if (!connection) {
|
||||
return {
|
||||
@@ -490,9 +497,10 @@ export class BatchExternalDbService {
|
||||
console.log(`[BatchExternalDbService] 외부 DB 특정 컬럼 조회: connectionId=${connectionId}, tableName=${tableName}, columns=[${columns.join(', ')}]`);
|
||||
|
||||
// 외부 DB 연결 정보 조회
|
||||
const connection = await prisma.external_db_connections.findUnique({
|
||||
where: { id: connectionId }
|
||||
});
|
||||
const connection = await queryOne<any>(
|
||||
`SELECT * FROM external_db_connections WHERE id = $1`,
|
||||
[connectionId]
|
||||
);
|
||||
|
||||
if (!connection) {
|
||||
return {
|
||||
@@ -569,9 +577,10 @@ export class BatchExternalDbService {
|
||||
}
|
||||
|
||||
// 외부 DB 연결 정보 조회
|
||||
const connection = await prisma.external_db_connections.findUnique({
|
||||
where: { id: connectionId }
|
||||
});
|
||||
const connection = await queryOne<any>(
|
||||
`SELECT * FROM external_db_connections WHERE id = $1`,
|
||||
[connectionId]
|
||||
);
|
||||
|
||||
if (!connection) {
|
||||
return {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// 배치관리 전용 서비스 (기존 소스와 완전 분리)
|
||||
// 작성일: 2024-12-24
|
||||
|
||||
import prisma from "../config/database";
|
||||
import { query, queryOne } from "../database/db";
|
||||
import { PasswordEncryption } from "../utils/passwordEncryption";
|
||||
import { DatabaseConnectorFactory } from "../database/DatabaseConnectorFactory";
|
||||
|
||||
@@ -49,16 +49,18 @@ export class BatchManagementService {
|
||||
});
|
||||
|
||||
// 활성화된 외부 DB 연결 조회
|
||||
const externalConnections = await prisma.external_db_connections.findMany({
|
||||
where: { is_active: 'Y' },
|
||||
select: {
|
||||
id: true,
|
||||
connection_name: true,
|
||||
db_type: true,
|
||||
description: true
|
||||
},
|
||||
orderBy: { connection_name: 'asc' }
|
||||
});
|
||||
const externalConnections = await query<{
|
||||
id: number;
|
||||
connection_name: string;
|
||||
db_type: string;
|
||||
description: string;
|
||||
}>(
|
||||
`SELECT id, connection_name, db_type, description
|
||||
FROM external_db_connections
|
||||
WHERE is_active = 'Y'
|
||||
ORDER BY connection_name ASC`,
|
||||
[]
|
||||
);
|
||||
|
||||
// 외부 DB 연결 추가
|
||||
externalConnections.forEach(conn => {
|
||||
@@ -97,13 +99,14 @@ export class BatchManagementService {
|
||||
|
||||
if (connectionType === 'internal') {
|
||||
// 내부 DB 테이블 조회
|
||||
const result = await prisma.$queryRaw<Array<{ table_name: string }>>`
|
||||
SELECT table_name
|
||||
const result = await query<{ table_name: string }>(
|
||||
`SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
AND table_type = 'BASE TABLE'
|
||||
ORDER BY table_name
|
||||
`;
|
||||
ORDER BY table_name`,
|
||||
[]
|
||||
);
|
||||
|
||||
tables = result.map(row => ({
|
||||
table_name: row.table_name,
|
||||
@@ -153,22 +156,23 @@ export class BatchManagementService {
|
||||
// 내부 DB 컬럼 조회
|
||||
console.log(`[BatchManagementService] 내부 DB 컬럼 조회 시작: ${tableName}`);
|
||||
|
||||
const result = await prisma.$queryRaw<Array<{
|
||||
const result = await query<{
|
||||
column_name: string;
|
||||
data_type: string;
|
||||
is_nullable: string;
|
||||
column_default: string | null
|
||||
}>>`
|
||||
SELECT
|
||||
}>(
|
||||
`SELECT
|
||||
column_name,
|
||||
data_type,
|
||||
is_nullable,
|
||||
column_default
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = ${tableName}
|
||||
ORDER BY ordinal_position
|
||||
`;
|
||||
AND table_name = $1
|
||||
ORDER BY ordinal_position`,
|
||||
[tableName]
|
||||
);
|
||||
|
||||
console.log(`[BatchManagementService] 쿼리 결과:`, result);
|
||||
|
||||
@@ -215,9 +219,10 @@ export class BatchManagementService {
|
||||
private static async getExternalTables(connectionId: number): Promise<BatchApiResponse<BatchTableInfo[]>> {
|
||||
try {
|
||||
// 연결 정보 조회
|
||||
const connection = await prisma.external_db_connections.findUnique({
|
||||
where: { id: connectionId }
|
||||
});
|
||||
const connection = await queryOne<any>(
|
||||
`SELECT * FROM external_db_connections WHERE id = $1`,
|
||||
[connectionId]
|
||||
);
|
||||
|
||||
if (!connection) {
|
||||
return {
|
||||
@@ -274,9 +279,10 @@ export class BatchManagementService {
|
||||
console.log(`[BatchManagementService] getExternalTableColumns 호출: connectionId=${connectionId}, tableName=${tableName}`);
|
||||
|
||||
// 연결 정보 조회
|
||||
const connection = await prisma.external_db_connections.findUnique({
|
||||
where: { id: connectionId }
|
||||
});
|
||||
const connection = await queryOne<any>(
|
||||
`SELECT * FROM external_db_connections WHERE id = $1`,
|
||||
[connectionId]
|
||||
);
|
||||
|
||||
if (!connection) {
|
||||
console.log(`[BatchManagementService] 연결 정보를 찾을 수 없음: connectionId=${connectionId}`);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// 작성일: 2024-12-24
|
||||
|
||||
import * as cron from 'node-cron';
|
||||
import prisma from '../config/database';
|
||||
import { query, queryOne } from '../database/db';
|
||||
import { BatchService } from './batchService';
|
||||
import { BatchExecutionLogService } from './batchExecutionLogService';
|
||||
import { logger } from '../utils/logger';
|
||||
@@ -59,14 +59,28 @@ export class BatchSchedulerService {
|
||||
*/
|
||||
private static async loadActiveBatchConfigs() {
|
||||
try {
|
||||
const activeConfigs = await prisma.batch_configs.findMany({
|
||||
where: {
|
||||
is_active: 'Y'
|
||||
},
|
||||
include: {
|
||||
batch_mappings: true
|
||||
}
|
||||
});
|
||||
const activeConfigs = await query<any>(
|
||||
`SELECT
|
||||
bc.*,
|
||||
json_agg(
|
||||
json_build_object(
|
||||
'id', bm.id,
|
||||
'batch_config_id', bm.batch_config_id,
|
||||
'field_name', bm.field_name,
|
||||
'source_field', bm.source_field,
|
||||
'field_type', bm.field_type,
|
||||
'is_required', bm.is_required,
|
||||
'default_value', bm.default_value,
|
||||
'transform_function', bm.transform_function,
|
||||
'sort_order', bm.sort_order
|
||||
)
|
||||
) FILTER (WHERE bm.id IS NOT NULL) as batch_mappings
|
||||
FROM batch_configs bc
|
||||
LEFT JOIN batch_mappings bm ON bc.id = bm.batch_config_id
|
||||
WHERE bc.is_active = 'Y'
|
||||
GROUP BY bc.id`,
|
||||
[]
|
||||
);
|
||||
|
||||
logger.info(`활성화된 배치 설정 ${activeConfigs.length}개 발견`);
|
||||
|
||||
@@ -153,10 +167,30 @@ export class BatchSchedulerService {
|
||||
await this.unscheduleBatchConfig(configId);
|
||||
|
||||
// 업데이트된 배치 설정 조회
|
||||
const config = await prisma.batch_configs.findUnique({
|
||||
where: { id: configId },
|
||||
include: { batch_mappings: true }
|
||||
});
|
||||
const configResult = await query<any>(
|
||||
`SELECT
|
||||
bc.*,
|
||||
json_agg(
|
||||
json_build_object(
|
||||
'id', bm.id,
|
||||
'batch_config_id', bm.batch_config_id,
|
||||
'field_name', bm.field_name,
|
||||
'source_field', bm.source_field,
|
||||
'field_type', bm.field_type,
|
||||
'is_required', bm.is_required,
|
||||
'default_value', bm.default_value,
|
||||
'transform_function', bm.transform_function,
|
||||
'sort_order', bm.sort_order
|
||||
)
|
||||
) FILTER (WHERE bm.id IS NOT NULL) as batch_mappings
|
||||
FROM batch_configs bc
|
||||
LEFT JOIN batch_mappings bm ON bc.id = bm.batch_config_id
|
||||
WHERE bc.id = $1
|
||||
GROUP BY bc.id`,
|
||||
[configId]
|
||||
);
|
||||
|
||||
const config = configResult[0] || null;
|
||||
|
||||
if (!config) {
|
||||
logger.warn(`배치 설정을 찾을 수 없습니다: ID ${configId}`);
|
||||
@@ -455,10 +489,11 @@ export class BatchSchedulerService {
|
||||
try {
|
||||
if (mapping.from_connection_type === 'internal') {
|
||||
// 내부 DB에서 조회
|
||||
const result = await prisma.$queryRawUnsafe(
|
||||
`SELECT * FROM ${mapping.from_table_name}`
|
||||
const result = await query<any>(
|
||||
`SELECT * FROM ${mapping.from_table_name}`,
|
||||
[]
|
||||
);
|
||||
return result as any[];
|
||||
return result;
|
||||
} else {
|
||||
// 외부 DB에서 조회 (구현 필요)
|
||||
logger.warn('외부 DB 조회는 아직 구현되지 않았습니다.');
|
||||
@@ -485,9 +520,13 @@ export class BatchSchedulerService {
|
||||
// 매핑된 컬럼만 추출
|
||||
const mappedData = this.mapColumns(record, mapping);
|
||||
|
||||
await prisma.$executeRawUnsafe(
|
||||
`INSERT INTO ${mapping.to_table_name} (${Object.keys(mappedData).join(', ')}) VALUES (${Object.values(mappedData).map(() => '?').join(', ')})`,
|
||||
...Object.values(mappedData)
|
||||
const columns = Object.keys(mappedData);
|
||||
const values = Object.values(mappedData);
|
||||
const placeholders = values.map((_, i) => `$${i + 1}`).join(', ');
|
||||
|
||||
await query(
|
||||
`INSERT INTO ${mapping.to_table_name} (${columns.join(', ')}) VALUES (${placeholders})`,
|
||||
values
|
||||
);
|
||||
successCount++;
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user