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:
kjs
2025-10-01 13:30:20 +09:00
parent 3d8f70e181
commit 505f656c15
6 changed files with 406 additions and 190 deletions

View File

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