타입스크립트 에러수정
This commit is contained in:
parent
7b84102cd9
commit
0d46bc540c
|
|
@ -1,6 +1,10 @@
|
||||||
import * as mysql from 'mysql2/promise';
|
import * as mysql from "mysql2/promise";
|
||||||
import { DatabaseConnector, ConnectionConfig, QueryResult } from '../interfaces/DatabaseConnector';
|
import {
|
||||||
import { ConnectionTestResult, TableInfo } from '../types/externalDbTypes';
|
DatabaseConnector,
|
||||||
|
ConnectionConfig,
|
||||||
|
QueryResult,
|
||||||
|
} from "../interfaces/DatabaseConnector";
|
||||||
|
import { ConnectionTestResult, TableInfo } from "../types/externalDbTypes";
|
||||||
|
|
||||||
export class MySQLConnector implements DatabaseConnector {
|
export class MySQLConnector implements DatabaseConnector {
|
||||||
private connection: mysql.Connection | null = null;
|
private connection: mysql.Connection | null = null;
|
||||||
|
|
@ -12,17 +16,17 @@ export class MySQLConnector implements DatabaseConnector {
|
||||||
|
|
||||||
async connect(): Promise<void> {
|
async connect(): Promise<void> {
|
||||||
if (this.connection) {
|
if (this.connection) {
|
||||||
throw new Error('이미 연결되어 있습니다.');
|
throw new Error("이미 연결되어 있습니다.");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.connection = await mysql.createConnection({
|
this.connection = await mysql.createConnection({
|
||||||
host: this.config.host,
|
host: this.config.host,
|
||||||
port: this.config.port,
|
port: this.config.port,
|
||||||
database: this.config.database,
|
database: this.config.database,
|
||||||
user: this.config.username,
|
user: this.config.user,
|
||||||
password: this.config.password,
|
password: this.config.password,
|
||||||
connectTimeout: this.config.connectionTimeout || 30000,
|
connectTimeout: this.config.connectionTimeoutMillis || 30000,
|
||||||
ssl: this.config.sslEnabled ? { rejectUnauthorized: false } : undefined,
|
ssl: this.config.ssl ? { rejectUnauthorized: false } : undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -37,30 +41,34 @@ export class MySQLConnector implements DatabaseConnector {
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
try {
|
try {
|
||||||
await this.connect();
|
await this.connect();
|
||||||
|
|
||||||
const [versionResult] = await this.connection!.query('SELECT VERSION() as version');
|
const [versionResult] = await this.connection!.query(
|
||||||
const [sizeResult] = await this.connection!.query(
|
"SELECT VERSION() as version"
|
||||||
'SELECT SUM(data_length + index_length) as size FROM information_schema.tables WHERE table_schema = DATABASE()'
|
|
||||||
);
|
);
|
||||||
|
const [sizeResult] = await this.connection!.query(
|
||||||
|
"SELECT SUM(data_length + index_length) as size FROM information_schema.tables WHERE table_schema = DATABASE()"
|
||||||
|
);
|
||||||
|
|
||||||
const responseTime = Date.now() - startTime;
|
const responseTime = Date.now() - startTime;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: 'MySQL 연결이 성공했습니다.',
|
message: "MySQL 연결이 성공했습니다.",
|
||||||
details: {
|
details: {
|
||||||
response_time: responseTime,
|
response_time: responseTime,
|
||||||
server_version: (versionResult as any)[0]?.version || '알 수 없음',
|
server_version: (versionResult as any)[0]?.version || "알 수 없음",
|
||||||
database_size: this.formatBytes(parseInt((sizeResult as any)[0]?.size || '0')),
|
database_size: this.formatBytes(
|
||||||
|
parseInt((sizeResult as any)[0]?.size || "0")
|
||||||
|
),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: 'MySQL 연결에 실패했습니다.',
|
message: "MySQL 연결에 실패했습니다.",
|
||||||
error: {
|
error: {
|
||||||
code: 'CONNECTION_FAILED',
|
code: "CONNECTION_FAILED",
|
||||||
details: error instanceof Error ? error.message : '알 수 없는 오류',
|
details: error instanceof Error ? error.message : "알 수 없는 오류",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
} finally {
|
} finally {
|
||||||
|
|
@ -78,9 +86,9 @@ export class MySQLConnector implements DatabaseConnector {
|
||||||
return {
|
return {
|
||||||
rows: rows as any[],
|
rows: rows as any[],
|
||||||
rowCount: Array.isArray(rows) ? rows.length : 0,
|
rowCount: Array.isArray(rows) ? rows.length : 0,
|
||||||
fields: (fields as mysql.FieldPacket[]).map(field => ({
|
fields: (fields as mysql.FieldPacket[]).map((field) => ({
|
||||||
name: field.name,
|
name: field.name,
|
||||||
dataType: field.type.toString(),
|
dataType: field.type?.toString() || "unknown",
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
} finally {
|
} finally {
|
||||||
|
|
@ -106,7 +114,8 @@ export class MySQLConnector implements DatabaseConnector {
|
||||||
const result: TableInfo[] = [];
|
const result: TableInfo[] = [];
|
||||||
|
|
||||||
for (const table of tables as any[]) {
|
for (const table of tables as any[]) {
|
||||||
const [columns] = await this.connection!.query(`
|
const [columns] = await this.connection!.query(
|
||||||
|
`
|
||||||
SELECT
|
SELECT
|
||||||
COLUMN_NAME as column_name,
|
COLUMN_NAME as column_name,
|
||||||
DATA_TYPE as data_type,
|
DATA_TYPE as data_type,
|
||||||
|
|
@ -116,7 +125,9 @@ export class MySQLConnector implements DatabaseConnector {
|
||||||
WHERE TABLE_SCHEMA = DATABASE()
|
WHERE TABLE_SCHEMA = DATABASE()
|
||||||
AND TABLE_NAME = ?
|
AND TABLE_NAME = ?
|
||||||
ORDER BY ORDINAL_POSITION
|
ORDER BY ORDINAL_POSITION
|
||||||
`, [table.table_name]);
|
`,
|
||||||
|
[table.table_name]
|
||||||
|
);
|
||||||
|
|
||||||
result.push({
|
result.push({
|
||||||
table_name: table.table_name,
|
table_name: table.table_name,
|
||||||
|
|
@ -131,18 +142,21 @@ export class MySQLConnector implements DatabaseConnector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getColumns(tableName: string): Promise<Array<{
|
async getColumns(tableName: string): Promise<
|
||||||
name: string;
|
Array<{
|
||||||
dataType: string;
|
name: string;
|
||||||
isNullable: boolean;
|
dataType: string;
|
||||||
defaultValue?: string;
|
isNullable: boolean;
|
||||||
}>> {
|
defaultValue?: string;
|
||||||
|
}>
|
||||||
|
> {
|
||||||
if (!this.connection) {
|
if (!this.connection) {
|
||||||
await this.connect();
|
await this.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const [columns] = await this.connection!.query(`
|
const [columns] = await this.connection!.query(
|
||||||
|
`
|
||||||
SELECT
|
SELECT
|
||||||
COLUMN_NAME as name,
|
COLUMN_NAME as name,
|
||||||
DATA_TYPE as dataType,
|
DATA_TYPE as dataType,
|
||||||
|
|
@ -152,9 +166,11 @@ export class MySQLConnector implements DatabaseConnector {
|
||||||
WHERE TABLE_SCHEMA = DATABASE()
|
WHERE TABLE_SCHEMA = DATABASE()
|
||||||
AND TABLE_NAME = ?
|
AND TABLE_NAME = ?
|
||||||
ORDER BY ORDINAL_POSITION
|
ORDER BY ORDINAL_POSITION
|
||||||
`, [tableName]);
|
`,
|
||||||
|
[tableName]
|
||||||
|
);
|
||||||
|
|
||||||
return (columns as any[]).map(col => ({
|
return (columns as any[]).map((col) => ({
|
||||||
name: col.name,
|
name: col.name,
|
||||||
dataType: col.dataType,
|
dataType: col.dataType,
|
||||||
isNullable: col.isNullable,
|
isNullable: col.isNullable,
|
||||||
|
|
@ -166,10 +182,10 @@ export class MySQLConnector implements DatabaseConnector {
|
||||||
}
|
}
|
||||||
|
|
||||||
private formatBytes(bytes: number): string {
|
private formatBytes(bytes: number): string {
|
||||||
if (bytes === 0) return '0 Bytes';
|
if (bytes === 0) return "0 Bytes";
|
||||||
const k = 1024;
|
const k = 1024;
|
||||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
|
||||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,275 @@
|
||||||
|
// 배치 관리 서비스
|
||||||
|
// 작성일: 2024-12-23
|
||||||
|
|
||||||
|
import { PrismaClient } from "@prisma/client";
|
||||||
|
import {
|
||||||
|
BatchJob,
|
||||||
|
BatchJobFilter,
|
||||||
|
BatchExecution,
|
||||||
|
BatchMonitoring,
|
||||||
|
} from "../types/batchManagement";
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
export class BatchService {
|
||||||
|
/**
|
||||||
|
* 배치 작업 목록 조회
|
||||||
|
*/
|
||||||
|
static async getBatchJobs(filter: BatchJobFilter): Promise<BatchJob[]> {
|
||||||
|
const whereCondition: any = {
|
||||||
|
company_code: filter.company_code || "*",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (filter.job_name) {
|
||||||
|
whereCondition.job_name = {
|
||||||
|
contains: filter.job_name,
|
||||||
|
mode: "insensitive",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.job_type) {
|
||||||
|
whereCondition.job_type = filter.job_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.is_active) {
|
||||||
|
whereCondition.is_active = filter.is_active === "Y";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.search) {
|
||||||
|
whereCondition.OR = [
|
||||||
|
{ job_name: { contains: filter.search, mode: "insensitive" } },
|
||||||
|
{ description: { contains: filter.search, mode: "insensitive" } },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const jobs = await prisma.batch_jobs.findMany({
|
||||||
|
where: whereCondition,
|
||||||
|
orderBy: { created_date: "desc" },
|
||||||
|
});
|
||||||
|
|
||||||
|
return jobs.map((job: any) => ({
|
||||||
|
...job,
|
||||||
|
is_active: job.is_active ? "Y" : "N",
|
||||||
|
})) as BatchJob[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 배치 작업 상세 조회
|
||||||
|
*/
|
||||||
|
static async getBatchJobById(id: number): Promise<BatchJob | null> {
|
||||||
|
const job = await prisma.batch_jobs.findUnique({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!job) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...job,
|
||||||
|
is_active: job.is_active ? "Y" : "N",
|
||||||
|
} as BatchJob;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 배치 작업 생성
|
||||||
|
*/
|
||||||
|
static async createBatchJob(data: BatchJob): Promise<BatchJob> {
|
||||||
|
const { id, config_json, ...createData } = data;
|
||||||
|
const job = await prisma.batch_jobs.create({
|
||||||
|
data: {
|
||||||
|
...createData,
|
||||||
|
is_active: data.is_active,
|
||||||
|
config_json: config_json || undefined,
|
||||||
|
created_date: new Date(),
|
||||||
|
updated_date: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...job,
|
||||||
|
is_active: job.is_active ? "Y" : "N",
|
||||||
|
} as BatchJob;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 배치 작업 수정
|
||||||
|
*/
|
||||||
|
static async updateBatchJob(
|
||||||
|
id: number,
|
||||||
|
data: Partial<BatchJob>
|
||||||
|
): Promise<BatchJob> {
|
||||||
|
const updateData: any = {
|
||||||
|
...data,
|
||||||
|
updated_date: new Date(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (data.is_active !== undefined) {
|
||||||
|
updateData.is_active = data.is_active;
|
||||||
|
}
|
||||||
|
|
||||||
|
const job = await prisma.batch_jobs.update({
|
||||||
|
where: { id },
|
||||||
|
data: updateData,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...job,
|
||||||
|
is_active: job.is_active ? "Y" : "N",
|
||||||
|
} as BatchJob;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 배치 작업 삭제
|
||||||
|
*/
|
||||||
|
static async deleteBatchJob(id: number): Promise<void> {
|
||||||
|
await prisma.batch_jobs.delete({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 배치 작업 수동 실행
|
||||||
|
*/
|
||||||
|
static async executeBatchJob(id: number): Promise<BatchExecution> {
|
||||||
|
const job = await prisma.batch_jobs.findUnique({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!job) {
|
||||||
|
throw new Error("배치 작업을 찾을 수 없습니다.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!job.is_active) {
|
||||||
|
throw new Error("비활성화된 배치 작업입니다.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 배치 실행 기록 생성
|
||||||
|
const execution = await prisma.batch_job_executions.create({
|
||||||
|
data: {
|
||||||
|
job_id: id,
|
||||||
|
execution_id: `exec_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
||||||
|
status: "RUNNING",
|
||||||
|
start_time: new Date(),
|
||||||
|
created_at: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 실제 배치 작업 실행 로직은 여기에 구현
|
||||||
|
// 현재는 시뮬레이션으로 처리
|
||||||
|
setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
// 배치 작업 시뮬레이션
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||||
|
|
||||||
|
await prisma.batch_job_executions.update({
|
||||||
|
where: { id: execution.id },
|
||||||
|
data: {
|
||||||
|
status: "SUCCESS",
|
||||||
|
end_time: new Date(),
|
||||||
|
exit_message: "배치 작업이 성공적으로 완료되었습니다.",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
await prisma.batch_job_executions.update({
|
||||||
|
where: { id: execution.id },
|
||||||
|
data: {
|
||||||
|
status: "FAILED",
|
||||||
|
end_time: new Date(),
|
||||||
|
exit_message:
|
||||||
|
error instanceof Error ? error.message : "알 수 없는 오류",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...execution,
|
||||||
|
execution_status: execution.status as any,
|
||||||
|
started_at: execution.start_time,
|
||||||
|
completed_at: execution.end_time,
|
||||||
|
error_message: execution.exit_message,
|
||||||
|
} as BatchExecution;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 배치 실행 목록 조회
|
||||||
|
*/
|
||||||
|
static async getBatchExecutions(jobId?: number): Promise<BatchExecution[]> {
|
||||||
|
const whereCondition: any = {};
|
||||||
|
|
||||||
|
if (jobId) {
|
||||||
|
whereCondition.job_id = jobId;
|
||||||
|
}
|
||||||
|
|
||||||
|
const executions = await prisma.batch_job_executions.findMany({
|
||||||
|
where: whereCondition,
|
||||||
|
orderBy: { start_time: "desc" },
|
||||||
|
// include 제거 - 관계가 정의되지 않음
|
||||||
|
});
|
||||||
|
|
||||||
|
return executions.map((exec: any) => ({
|
||||||
|
...exec,
|
||||||
|
execution_status: exec.status,
|
||||||
|
started_at: exec.start_time,
|
||||||
|
completed_at: exec.end_time,
|
||||||
|
error_message: exec.exit_message,
|
||||||
|
})) as BatchExecution[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 배치 모니터링 정보 조회
|
||||||
|
*/
|
||||||
|
static async getBatchMonitoring(): Promise<BatchMonitoring> {
|
||||||
|
const totalJobs = await prisma.batch_jobs.count();
|
||||||
|
const activeJobs = await prisma.batch_jobs.count({
|
||||||
|
where: { is_active: "Y" },
|
||||||
|
});
|
||||||
|
|
||||||
|
const runningExecutions = await prisma.batch_job_executions.count({
|
||||||
|
where: { status: "RUNNING" },
|
||||||
|
});
|
||||||
|
|
||||||
|
const recentExecutions = await prisma.batch_job_executions.findMany({
|
||||||
|
where: {
|
||||||
|
created_at: {
|
||||||
|
gte: new Date(Date.now() - 24 * 60 * 60 * 1000), // 최근 24시간
|
||||||
|
},
|
||||||
|
},
|
||||||
|
orderBy: { start_time: "desc" },
|
||||||
|
take: 10,
|
||||||
|
// include 제거 - 관계가 정의되지 않음
|
||||||
|
});
|
||||||
|
|
||||||
|
const successCount = await prisma.batch_job_executions.count({
|
||||||
|
where: {
|
||||||
|
status: "SUCCESS",
|
||||||
|
start_time: {
|
||||||
|
gte: new Date(Date.now() - 24 * 60 * 60 * 1000),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const failedCount = await prisma.batch_job_executions.count({
|
||||||
|
where: {
|
||||||
|
status: "FAILED",
|
||||||
|
start_time: {
|
||||||
|
gte: new Date(Date.now() - 24 * 60 * 60 * 1000),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
total_jobs: totalJobs,
|
||||||
|
active_jobs: activeJobs,
|
||||||
|
running_jobs: runningExecutions,
|
||||||
|
failed_jobs_today: failedCount,
|
||||||
|
successful_jobs_today: successCount,
|
||||||
|
recent_executions: recentExecutions.map((exec: any) => ({
|
||||||
|
...exec,
|
||||||
|
execution_status: exec.status,
|
||||||
|
started_at: exec.start_time,
|
||||||
|
completed_at: exec.end_time,
|
||||||
|
error_message: exec.exit_message,
|
||||||
|
})) as BatchExecution[],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,253 @@
|
||||||
|
// 수집 관리 서비스
|
||||||
|
// 작성일: 2024-12-23
|
||||||
|
|
||||||
|
import { PrismaClient } from "@prisma/client";
|
||||||
|
import {
|
||||||
|
DataCollectionConfig,
|
||||||
|
CollectionFilter,
|
||||||
|
CollectionJob,
|
||||||
|
CollectionHistory,
|
||||||
|
} from "../types/collectionManagement";
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
export class CollectionService {
|
||||||
|
/**
|
||||||
|
* 수집 설정 목록 조회
|
||||||
|
*/
|
||||||
|
static async getCollectionConfigs(
|
||||||
|
filter: CollectionFilter
|
||||||
|
): Promise<DataCollectionConfig[]> {
|
||||||
|
const whereCondition: any = {
|
||||||
|
company_code: filter.company_code || "*",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (filter.config_name) {
|
||||||
|
whereCondition.config_name = {
|
||||||
|
contains: filter.config_name,
|
||||||
|
mode: "insensitive",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.source_connection_id) {
|
||||||
|
whereCondition.source_connection_id = filter.source_connection_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.collection_type) {
|
||||||
|
whereCondition.collection_type = filter.collection_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.is_active) {
|
||||||
|
whereCondition.is_active = filter.is_active === "Y";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.search) {
|
||||||
|
whereCondition.OR = [
|
||||||
|
{ config_name: { contains: filter.search, mode: "insensitive" } },
|
||||||
|
{ description: { contains: filter.search, mode: "insensitive" } },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const configs = await prisma.data_collection_configs.findMany({
|
||||||
|
where: whereCondition,
|
||||||
|
orderBy: { created_date: "desc" },
|
||||||
|
});
|
||||||
|
|
||||||
|
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 prisma.data_collection_configs.findUnique({
|
||||||
|
where: { 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 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(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...config,
|
||||||
|
is_active: config.is_active ? "Y" : "N",
|
||||||
|
} as DataCollectionConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 수집 설정 수정
|
||||||
|
*/
|
||||||
|
static async updateCollectionConfig(
|
||||||
|
id: number,
|
||||||
|
data: Partial<DataCollectionConfig>
|
||||||
|
): Promise<DataCollectionConfig> {
|
||||||
|
const updateData: any = {
|
||||||
|
...data,
|
||||||
|
updated_date: new Date(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (data.is_active !== undefined) {
|
||||||
|
updateData.is_active = data.is_active;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = await prisma.data_collection_configs.update({
|
||||||
|
where: { id },
|
||||||
|
data: updateData,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...config,
|
||||||
|
is_active: config.is_active ? "Y" : "N",
|
||||||
|
} as DataCollectionConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 수집 설정 삭제
|
||||||
|
*/
|
||||||
|
static async deleteCollectionConfig(id: number): Promise<void> {
|
||||||
|
await prisma.data_collection_configs.delete({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 수집 작업 실행
|
||||||
|
*/
|
||||||
|
static async executeCollection(configId: number): Promise<CollectionJob> {
|
||||||
|
const config = await prisma.data_collection_configs.findUnique({
|
||||||
|
where: { id: configId },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!config) {
|
||||||
|
throw new Error("수집 설정을 찾을 수 없습니다.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.is_active) {
|
||||||
|
throw new Error("비활성화된 수집 설정입니다.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 수집 작업 기록 생성
|
||||||
|
const job = await prisma.data_collection_jobs.create({
|
||||||
|
data: {
|
||||||
|
config_id: configId,
|
||||||
|
job_status: "running",
|
||||||
|
started_at: new Date(),
|
||||||
|
created_date: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 실제 수집 작업 실행 로직은 여기에 구현
|
||||||
|
// 현재는 시뮬레이션으로 처리
|
||||||
|
setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
// 수집 작업 시뮬레이션
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} 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 : "알 수 없는 오류",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
return job as CollectionJob;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 수집 작업 목록 조회
|
||||||
|
*/
|
||||||
|
static async getCollectionJobs(configId?: number): Promise<CollectionJob[]> {
|
||||||
|
const whereCondition: any = {};
|
||||||
|
|
||||||
|
if (configId) {
|
||||||
|
whereCondition.config_id = configId;
|
||||||
|
}
|
||||||
|
|
||||||
|
const jobs = await prisma.data_collection_jobs.findMany({
|
||||||
|
where: whereCondition,
|
||||||
|
orderBy: { started_at: "desc" },
|
||||||
|
include: {
|
||||||
|
config: {
|
||||||
|
select: {
|
||||||
|
config_name: true,
|
||||||
|
collection_type: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return jobs as CollectionJob[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 수집 이력 조회
|
||||||
|
*/
|
||||||
|
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개 이력
|
||||||
|
});
|
||||||
|
|
||||||
|
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[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -471,15 +471,7 @@ export class DataflowService {
|
||||||
const linkedData = await prisma.data_relationship_bridge.findMany({
|
const linkedData = await prisma.data_relationship_bridge.findMany({
|
||||||
where: whereCondition,
|
where: whereCondition,
|
||||||
orderBy: { created_at: "desc" },
|
orderBy: { created_at: "desc" },
|
||||||
include: {
|
// include 제거 - relationship 관계가 스키마에 정의되지 않음
|
||||||
relationship: {
|
|
||||||
select: {
|
|
||||||
relationship_name: true,
|
|
||||||
relationship_type: true,
|
|
||||||
connection_type: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
|
|
@ -520,15 +512,7 @@ export class DataflowService {
|
||||||
const linkedData = await prisma.data_relationship_bridge.findMany({
|
const linkedData = await prisma.data_relationship_bridge.findMany({
|
||||||
where: whereCondition,
|
where: whereCondition,
|
||||||
orderBy: { created_at: "desc" },
|
orderBy: { created_at: "desc" },
|
||||||
include: {
|
// include 제거 - relationship 관계가 스키마에 정의되지 않음
|
||||||
relationship: {
|
|
||||||
select: {
|
|
||||||
relationship_name: true,
|
|
||||||
relationship_type: true,
|
|
||||||
connection_type: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@ export type ComponentType = "container" | "row" | "column" | "widget" | "group";
|
||||||
|
|
||||||
// 웹 타입 정의
|
// 웹 타입 정의
|
||||||
// WebType은 통합 타입에서 import (중복 정의 제거)
|
// WebType은 통합 타입에서 import (중복 정의 제거)
|
||||||
export { WebType } from "./unified-web-types";
|
import { WebType } from "./unified-web-types";
|
||||||
|
export { WebType };
|
||||||
|
|
||||||
// 위치 정보
|
// 위치 정보
|
||||||
export interface Position {
|
export interface Position {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue