297 lines
8.3 KiB
TypeScript
297 lines
8.3 KiB
TypeScript
// 수집 관리 서비스
|
|
// 작성일: 2024-12-23
|
|
|
|
import { query, queryOne, transaction } from "../database/db";
|
|
import {
|
|
DataCollectionConfig,
|
|
CollectionFilter,
|
|
CollectionJob,
|
|
CollectionHistory,
|
|
} from "../types/collectionManagement";
|
|
|
|
export class CollectionService {
|
|
/**
|
|
* 수집 설정 목록 조회
|
|
*/
|
|
static async getCollectionConfigs(
|
|
filter: CollectionFilter
|
|
): Promise<DataCollectionConfig[]> {
|
|
const whereConditions: string[] = ["company_code = $1"];
|
|
const values: any[] = [filter.company_code || "*"];
|
|
let paramIndex = 2;
|
|
|
|
if (filter.config_name) {
|
|
whereConditions.push(`config_name ILIKE $${paramIndex++}`);
|
|
values.push(`%${filter.config_name}%`);
|
|
}
|
|
|
|
if (filter.source_connection_id) {
|
|
whereConditions.push(`source_connection_id = $${paramIndex++}`);
|
|
values.push(filter.source_connection_id);
|
|
}
|
|
|
|
if (filter.collection_type) {
|
|
whereConditions.push(`collection_type = $${paramIndex++}`);
|
|
values.push(filter.collection_type);
|
|
}
|
|
|
|
if (filter.is_active) {
|
|
whereConditions.push(`is_active = $${paramIndex++}`);
|
|
values.push(filter.is_active === "Y");
|
|
}
|
|
|
|
if (filter.search) {
|
|
whereConditions.push(
|
|
`(config_name ILIKE $${paramIndex} OR description ILIKE $${paramIndex})`
|
|
);
|
|
values.push(`%${filter.search}%`);
|
|
paramIndex++;
|
|
}
|
|
|
|
const configs = await query<any>(
|
|
`SELECT * FROM data_collection_configs
|
|
WHERE ${whereConditions.join(" AND ")}
|
|
ORDER BY created_date DESC`,
|
|
values
|
|
);
|
|
|
|
return configs.map((config: any) => ({
|
|
...config,
|
|
is_active: config.is_active ? "Y" : "N",
|
|
})) as DataCollectionConfig[];
|
|
}
|
|
|
|
/**
|
|
* 수집 설정 상세 조회
|
|
*/
|
|
static async getCollectionConfigById(
|
|
id: number
|
|
): Promise<DataCollectionConfig | null> {
|
|
const config = await queryOne<any>(
|
|
`SELECT * FROM data_collection_configs WHERE id = $1`,
|
|
[id]
|
|
);
|
|
|
|
if (!config) return null;
|
|
|
|
return {
|
|
...config,
|
|
is_active: config.is_active ? "Y" : "N",
|
|
} as DataCollectionConfig;
|
|
}
|
|
|
|
/**
|
|
* 수집 설정 생성
|
|
*/
|
|
static async createCollectionConfig(
|
|
data: DataCollectionConfig
|
|
): Promise<DataCollectionConfig> {
|
|
const { id, collection_options, ...createData } = data;
|
|
const config = await queryOne<any>(
|
|
`INSERT INTO data_collection_configs
|
|
(config_name, company_code, source_connection_id, collection_type,
|
|
collection_options, schedule_cron, is_active, description,
|
|
created_by, updated_by, created_date, updated_date)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, NOW(), NOW())
|
|
RETURNING *`,
|
|
[
|
|
createData.config_name,
|
|
createData.company_code,
|
|
createData.source_connection_id,
|
|
createData.collection_type,
|
|
collection_options ? JSON.stringify(collection_options) : null,
|
|
createData.schedule_cron,
|
|
data.is_active,
|
|
createData.description,
|
|
createData.created_by,
|
|
createData.updated_by,
|
|
]
|
|
);
|
|
|
|
return {
|
|
...config,
|
|
is_active: config.is_active ? "Y" : "N",
|
|
} as DataCollectionConfig;
|
|
}
|
|
|
|
/**
|
|
* 수집 설정 수정
|
|
*/
|
|
static async updateCollectionConfig(
|
|
id: number,
|
|
data: Partial<DataCollectionConfig>
|
|
): Promise<DataCollectionConfig> {
|
|
const updateFields: string[] = ["updated_date = NOW()"];
|
|
const values: any[] = [];
|
|
let paramIndex = 1;
|
|
|
|
if (data.config_name !== undefined) {
|
|
updateFields.push(`config_name = $${paramIndex++}`);
|
|
values.push(data.config_name);
|
|
}
|
|
if (data.source_connection_id !== undefined) {
|
|
updateFields.push(`source_connection_id = $${paramIndex++}`);
|
|
values.push(data.source_connection_id);
|
|
}
|
|
if (data.collection_type !== undefined) {
|
|
updateFields.push(`collection_type = $${paramIndex++}`);
|
|
values.push(data.collection_type);
|
|
}
|
|
if (data.collection_options !== undefined) {
|
|
updateFields.push(`collection_options = $${paramIndex++}`);
|
|
values.push(
|
|
data.collection_options ? JSON.stringify(data.collection_options) : null
|
|
);
|
|
}
|
|
if (data.schedule_cron !== undefined) {
|
|
updateFields.push(`schedule_cron = $${paramIndex++}`);
|
|
values.push(data.schedule_cron);
|
|
}
|
|
if (data.is_active !== undefined) {
|
|
updateFields.push(`is_active = $${paramIndex++}`);
|
|
values.push(data.is_active);
|
|
}
|
|
if (data.description !== undefined) {
|
|
updateFields.push(`description = $${paramIndex++}`);
|
|
values.push(data.description);
|
|
}
|
|
if (data.updated_by !== undefined) {
|
|
updateFields.push(`updated_by = $${paramIndex++}`);
|
|
values.push(data.updated_by);
|
|
}
|
|
|
|
const config = await queryOne<any>(
|
|
`UPDATE data_collection_configs
|
|
SET ${updateFields.join(", ")}
|
|
WHERE id = $${paramIndex}
|
|
RETURNING *`,
|
|
[...values, id]
|
|
);
|
|
|
|
return {
|
|
...config,
|
|
is_active: config.is_active ? "Y" : "N",
|
|
} as DataCollectionConfig;
|
|
}
|
|
|
|
/**
|
|
* 수집 설정 삭제
|
|
*/
|
|
static async deleteCollectionConfig(id: number): Promise<void> {
|
|
await query(`DELETE FROM data_collection_configs WHERE id = $1`, [id]);
|
|
}
|
|
|
|
/**
|
|
* 수집 작업 실행
|
|
*/
|
|
static async executeCollection(configId: number): Promise<CollectionJob> {
|
|
const config = await queryOne<any>(
|
|
`SELECT * FROM data_collection_configs WHERE id = $1`,
|
|
[configId]
|
|
);
|
|
|
|
if (!config) {
|
|
throw new Error("수집 설정을 찾을 수 없습니다.");
|
|
}
|
|
|
|
if (!config.is_active) {
|
|
throw new Error("비활성화된 수집 설정입니다.");
|
|
}
|
|
|
|
// 수집 작업 기록 생성
|
|
const job = await queryOne<any>(
|
|
`INSERT INTO data_collection_jobs
|
|
(config_id, job_status, started_at, created_date)
|
|
VALUES ($1, $2, NOW(), NOW())
|
|
RETURNING *`,
|
|
[configId, "running"]
|
|
);
|
|
|
|
// 실제 수집 작업 실행 로직은 여기에 구현
|
|
// 현재는 시뮬레이션으로 처리
|
|
setTimeout(async () => {
|
|
try {
|
|
// 수집 작업 시뮬레이션
|
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
|
|
const recordsCollected = Math.floor(Math.random() * 1000) + 100;
|
|
|
|
await query(
|
|
`UPDATE data_collection_jobs
|
|
SET job_status = $1, completed_at = NOW(), records_processed = $2
|
|
WHERE id = $3`,
|
|
["completed", recordsCollected, job.id]
|
|
);
|
|
} catch (error) {
|
|
await query(
|
|
`UPDATE data_collection_jobs
|
|
SET job_status = $1, completed_at = NOW(), error_message = $2
|
|
WHERE id = $3`,
|
|
[
|
|
"failed",
|
|
error instanceof Error ? error.message : "알 수 없는 오류",
|
|
job.id,
|
|
]
|
|
);
|
|
}
|
|
}, 0);
|
|
|
|
return job as CollectionJob;
|
|
}
|
|
|
|
/**
|
|
* 수집 작업 목록 조회
|
|
*/
|
|
static async getCollectionJobs(configId?: number): Promise<CollectionJob[]> {
|
|
let sql = `
|
|
SELECT j.*, c.config_name, c.collection_type
|
|
FROM data_collection_jobs j
|
|
LEFT JOIN data_collection_configs c ON j.config_id = c.id
|
|
`;
|
|
const values: any[] = [];
|
|
|
|
if (configId) {
|
|
sql += ` WHERE j.config_id = $1`;
|
|
values.push(configId);
|
|
}
|
|
|
|
sql += ` ORDER BY j.started_at DESC`;
|
|
|
|
const jobs = await query<any>(sql, values);
|
|
|
|
return jobs as CollectionJob[];
|
|
}
|
|
|
|
/**
|
|
* 수집 이력 조회
|
|
*/
|
|
static async getCollectionHistory(
|
|
configId: number
|
|
): Promise<CollectionHistory[]> {
|
|
const history = await query<any>(
|
|
`SELECT * FROM data_collection_jobs
|
|
WHERE config_id = $1
|
|
ORDER BY started_at DESC
|
|
LIMIT 50`,
|
|
[configId]
|
|
);
|
|
|
|
return history.map((item: any) => ({
|
|
id: item.id,
|
|
config_id: item.config_id,
|
|
status: item.job_status,
|
|
collection_date: item.started_at,
|
|
started_at: item.started_at,
|
|
completed_at: item.completed_at,
|
|
execution_time_ms:
|
|
item.completed_at && item.started_at
|
|
? new Date(item.completed_at).getTime() -
|
|
new Date(item.started_at).getTime()
|
|
: null,
|
|
records_collected: item.records_processed || 0,
|
|
result_message: `${item.records_processed || 0}개의 레코드가 처리되었습니다.`,
|
|
error_message: item.error_message,
|
|
})) as CollectionHistory[];
|
|
}
|
|
}
|