feat: Phase 3.6 CollectionService 전환 완료 및 Phase 3.7-3.9 계획서 작성
CollectionService 전환 완료: - 11개 Prisma 호출을 모두 Raw Query로 전환 - 수집 설정 CRUD (getCollectionConfigs, getCollectionConfigById, createCollectionConfig, updateCollectionConfig, deleteCollectionConfig) - 수집 작업 관리 (executeCollection, getCollectionJobs, getCollectionHistory) - 동적 WHERE 조건 생성 (ILIKE 검색, OR 조건) - 동적 UPDATE 쿼리 (변경된 필드만 업데이트) - JSON 필드 처리 (collection_options) - LEFT JOIN (작업 목록 조회 시 설정 정보 포함) - 비동기 작업 처리 (setTimeout 내 query 사용) - 필드명 수정 (schedule_expression → schedule_cron) - TypeScript 컴파일 성공 - Prisma import 완전 제거 Phase 3 남은 서비스 계획서 작성: - PHASE3.7_LAYOUT_SERVICE_MIGRATION.md (10개 호출) - 레이아웃 표준 관리 (CRUD, 통계, JSON 필드) - PHASE3.8_DB_TYPE_CATEGORY_SERVICE_MIGRATION.md (10개 호출) - DB 타입 카테고리 관리 (CRUD, 통계, UPSERT) - PHASE3.9_TEMPLATE_STANDARD_SERVICE_MIGRATION.md (6개 호출) - 템플릿 표준 관리 (복합 키, JSON 필드, DISTINCT) Phase 3 진행률: 87/162 (53.7%) 전체 진행률: 338/444 (76.1%)
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// 수집 관리 서비스
|
||||
// 작성일: 2024-12-23
|
||||
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import { query, queryOne, transaction } from "../database/db";
|
||||
import {
|
||||
DataCollectionConfig,
|
||||
CollectionFilter,
|
||||
@@ -9,8 +9,6 @@ import {
|
||||
CollectionHistory,
|
||||
} from "../types/collectionManagement";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export class CollectionService {
|
||||
/**
|
||||
* 수집 설정 목록 조회
|
||||
@@ -18,40 +16,44 @@ export class CollectionService {
|
||||
static async getCollectionConfigs(
|
||||
filter: CollectionFilter
|
||||
): Promise<DataCollectionConfig[]> {
|
||||
const whereCondition: any = {
|
||||
company_code: filter.company_code || "*",
|
||||
};
|
||||
const whereConditions: string[] = ["company_code = $1"];
|
||||
const values: any[] = [filter.company_code || "*"];
|
||||
let paramIndex = 2;
|
||||
|
||||
if (filter.config_name) {
|
||||
whereCondition.config_name = {
|
||||
contains: filter.config_name,
|
||||
mode: "insensitive",
|
||||
};
|
||||
whereConditions.push(`config_name ILIKE $${paramIndex++}`);
|
||||
values.push(`%${filter.config_name}%`);
|
||||
}
|
||||
|
||||
if (filter.source_connection_id) {
|
||||
whereCondition.source_connection_id = filter.source_connection_id;
|
||||
whereConditions.push(`source_connection_id = $${paramIndex++}`);
|
||||
values.push(filter.source_connection_id);
|
||||
}
|
||||
|
||||
if (filter.collection_type) {
|
||||
whereCondition.collection_type = filter.collection_type;
|
||||
whereConditions.push(`collection_type = $${paramIndex++}`);
|
||||
values.push(filter.collection_type);
|
||||
}
|
||||
|
||||
if (filter.is_active) {
|
||||
whereCondition.is_active = filter.is_active === "Y";
|
||||
whereConditions.push(`is_active = $${paramIndex++}`);
|
||||
values.push(filter.is_active === "Y");
|
||||
}
|
||||
|
||||
if (filter.search) {
|
||||
whereCondition.OR = [
|
||||
{ config_name: { contains: filter.search, mode: "insensitive" } },
|
||||
{ description: { contains: filter.search, mode: "insensitive" } },
|
||||
];
|
||||
whereConditions.push(
|
||||
`(config_name ILIKE $${paramIndex} OR description ILIKE $${paramIndex})`
|
||||
);
|
||||
values.push(`%${filter.search}%`);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
const configs = await prisma.data_collection_configs.findMany({
|
||||
where: whereCondition,
|
||||
orderBy: { created_date: "desc" },
|
||||
});
|
||||
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,
|
||||
@@ -65,9 +67,10 @@ export class CollectionService {
|
||||
static async getCollectionConfigById(
|
||||
id: number
|
||||
): Promise<DataCollectionConfig | null> {
|
||||
const config = await prisma.data_collection_configs.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
const config = await queryOne<any>(
|
||||
`SELECT * FROM data_collection_configs WHERE id = $1`,
|
||||
[id]
|
||||
);
|
||||
|
||||
if (!config) return null;
|
||||
|
||||
@@ -84,15 +87,26 @@ export class CollectionService {
|
||||
data: DataCollectionConfig
|
||||
): Promise<DataCollectionConfig> {
|
||||
const { id, collection_options, ...createData } = data;
|
||||
const config = await prisma.data_collection_configs.create({
|
||||
data: {
|
||||
...createData,
|
||||
is_active: data.is_active,
|
||||
collection_options: collection_options || undefined,
|
||||
created_date: new Date(),
|
||||
updated_date: new Date(),
|
||||
},
|
||||
});
|
||||
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,
|
||||
@@ -107,19 +121,52 @@ export class CollectionService {
|
||||
id: number,
|
||||
data: Partial<DataCollectionConfig>
|
||||
): Promise<DataCollectionConfig> {
|
||||
const updateData: any = {
|
||||
...data,
|
||||
updated_date: new Date(),
|
||||
};
|
||||
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) {
|
||||
updateData.is_active = data.is_active;
|
||||
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 prisma.data_collection_configs.update({
|
||||
where: { id },
|
||||
data: updateData,
|
||||
});
|
||||
const config = await queryOne<any>(
|
||||
`UPDATE data_collection_configs
|
||||
SET ${updateFields.join(", ")}
|
||||
WHERE id = $${paramIndex}
|
||||
RETURNING *`,
|
||||
[...values, id]
|
||||
);
|
||||
|
||||
return {
|
||||
...config,
|
||||
@@ -131,18 +178,17 @@ export class CollectionService {
|
||||
* 수집 설정 삭제
|
||||
*/
|
||||
static async deleteCollectionConfig(id: number): Promise<void> {
|
||||
await prisma.data_collection_configs.delete({
|
||||
where: { id },
|
||||
});
|
||||
await query(`DELETE FROM data_collection_configs WHERE id = $1`, [id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 수집 작업 실행
|
||||
*/
|
||||
static async executeCollection(configId: number): Promise<CollectionJob> {
|
||||
const config = await prisma.data_collection_configs.findUnique({
|
||||
where: { id: configId },
|
||||
});
|
||||
const config = await queryOne<any>(
|
||||
`SELECT * FROM data_collection_configs WHERE id = $1`,
|
||||
[configId]
|
||||
);
|
||||
|
||||
if (!config) {
|
||||
throw new Error("수집 설정을 찾을 수 없습니다.");
|
||||
@@ -153,14 +199,13 @@ export class CollectionService {
|
||||
}
|
||||
|
||||
// 수집 작업 기록 생성
|
||||
const job = await prisma.data_collection_jobs.create({
|
||||
data: {
|
||||
config_id: configId,
|
||||
job_status: "running",
|
||||
started_at: new Date(),
|
||||
created_date: new Date(),
|
||||
},
|
||||
});
|
||||
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"]
|
||||
);
|
||||
|
||||
// 실제 수집 작업 실행 로직은 여기에 구현
|
||||
// 현재는 시뮬레이션으로 처리
|
||||
@@ -171,24 +216,23 @@ export class CollectionService {
|
||||
|
||||
const recordsCollected = Math.floor(Math.random() * 1000) + 100;
|
||||
|
||||
await prisma.data_collection_jobs.update({
|
||||
where: { id: job.id },
|
||||
data: {
|
||||
job_status: "completed",
|
||||
completed_at: new Date(),
|
||||
records_processed: recordsCollected,
|
||||
},
|
||||
});
|
||||
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 prisma.data_collection_jobs.update({
|
||||
where: { id: job.id },
|
||||
data: {
|
||||
job_status: "failed",
|
||||
completed_at: new Date(),
|
||||
error_message:
|
||||
error instanceof Error ? error.message : "알 수 없는 오류",
|
||||
},
|
||||
});
|
||||
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);
|
||||
|
||||
@@ -199,24 +243,21 @@ export class CollectionService {
|
||||
* 수집 작업 목록 조회
|
||||
*/
|
||||
static async getCollectionJobs(configId?: number): Promise<CollectionJob[]> {
|
||||
const whereCondition: any = {};
|
||||
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) {
|
||||
whereCondition.config_id = configId;
|
||||
sql += ` WHERE j.config_id = $1`;
|
||||
values.push(configId);
|
||||
}
|
||||
|
||||
const jobs = await prisma.data_collection_jobs.findMany({
|
||||
where: whereCondition,
|
||||
orderBy: { started_at: "desc" },
|
||||
include: {
|
||||
config: {
|
||||
select: {
|
||||
config_name: true,
|
||||
collection_type: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
sql += ` ORDER BY j.started_at DESC`;
|
||||
|
||||
const jobs = await query<any>(sql, values);
|
||||
|
||||
return jobs as CollectionJob[];
|
||||
}
|
||||
@@ -227,11 +268,13 @@ export class CollectionService {
|
||||
static async getCollectionHistory(
|
||||
configId: number
|
||||
): Promise<CollectionHistory[]> {
|
||||
const history = await prisma.data_collection_jobs.findMany({
|
||||
where: { config_id: configId },
|
||||
orderBy: { started_at: "desc" },
|
||||
take: 50, // 최근 50개 이력
|
||||
});
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user