ERP-node/backend-node/src/services/mapDataService.ts

230 lines
6.0 KiB
TypeScript
Raw Normal View History

import { logger } from "../utils/logger";
import { query } from "../database/db";
import { ExternalDbConnectionService } from "./externalDbConnectionService";
interface MapDataQuery {
connectionId?: number;
tableName: string;
latColumn: string;
lngColumn: string;
labelColumn?: string;
statusColumn?: string;
additionalColumns?: string[];
whereClause?: string;
}
export interface MapMarker {
id: string | number;
latitude: number;
longitude: number;
label?: string;
status?: string;
additionalInfo?: Record<string, any>;
}
/**
*
* / DB에서 /
*/
export class MapDataService {
constructor() {
// ExternalDbConnectionService는 static 메서드를 사용
}
/**
* DB에서
*/
async getMapData(params: MapDataQuery): Promise<MapMarker[]> {
try {
logger.info("🗺️ 외부 DB 지도 데이터 조회 시작:", params);
// SELECT할 컬럼 목록 구성
const selectColumns = [
params.latColumn,
params.lngColumn,
params.labelColumn,
params.statusColumn,
...(params.additionalColumns || []),
].filter(Boolean);
// 중복 제거
const uniqueColumns = Array.from(new Set(selectColumns));
// SQL 쿼리 구성
let sql = `SELECT ${uniqueColumns.map((col) => `"${col}"`).join(", ")} FROM "${params.tableName}"`;
if (params.whereClause) {
sql += ` WHERE ${params.whereClause}`;
}
logger.info("📝 실행할 SQL:", sql);
// 외부 DB 쿼리 실행 (static 메서드 사용)
const result = await ExternalDbConnectionService.executeQuery(
params.connectionId!,
sql
);
if (!result.success || !result.data) {
throw new Error("외부 DB 쿼리 실패");
}
// 데이터를 MapMarker 형식으로 변환
const markers = this.convertToMarkers(
result.data,
params.latColumn,
params.lngColumn,
params.labelColumn,
params.statusColumn,
params.additionalColumns
);
logger.info(`${markers.length}개의 마커 데이터 변환 완료`);
return markers;
} catch (error) {
logger.error("❌ 외부 DB 지도 데이터 조회 오류:", error);
throw error;
}
}
/**
* DB에서
*/
async getInternalMapData(
params: Omit<MapDataQuery, "connectionId">
): Promise<MapMarker[]> {
try {
logger.info("🗺️ 내부 DB 지도 데이터 조회 시작:", params);
// SELECT할 컬럼 목록 구성
const selectColumns = [
params.latColumn,
params.lngColumn,
params.labelColumn,
params.statusColumn,
...(params.additionalColumns || []),
].filter(Boolean);
// 중복 제거
const uniqueColumns = Array.from(new Set(selectColumns));
// SQL 쿼리 구성
let sql = `SELECT ${uniqueColumns.map((col) => `"${col}"`).join(", ")} FROM "${params.tableName}"`;
if (params.whereClause) {
sql += ` WHERE ${params.whereClause}`;
}
logger.info("📝 실행할 SQL:", sql);
// 내부 DB 쿼리 실행
const rows = await query(sql);
// 데이터를 MapMarker 형식으로 변환
const markers = this.convertToMarkers(
rows,
params.latColumn,
params.lngColumn,
params.labelColumn,
params.statusColumn,
params.additionalColumns
);
logger.info(`${markers.length}개의 마커 데이터 변환 완료`);
return markers;
} catch (error) {
logger.error("❌ 내부 DB 지도 데이터 조회 오류:", error);
throw error;
}
}
/**
* DB MapMarker
*/
private convertToMarkers(
data: any[],
latColumn: string,
lngColumn: string,
labelColumn?: string,
statusColumn?: string,
additionalColumns?: string[]
): MapMarker[] {
const markers: MapMarker[] = [];
for (let i = 0; i < data.length; i++) {
const row = data[i];
// 위도/경도 추출 (다양한 컬럼명 지원)
const lat = this.extractCoordinate(row, latColumn);
const lng = this.extractCoordinate(row, lngColumn);
// 유효한 좌표인지 확인
if (lat === null || lng === null || isNaN(lat) || isNaN(lng)) {
logger.warn(`⚠️ 유효하지 않은 좌표 스킵: row ${i}`, { lat, lng });
continue;
}
// 위도 범위 체크 (-90 ~ 90)
if (lat < -90 || lat > 90) {
logger.warn(`⚠️ 위도 범위 초과: ${lat}`);
continue;
}
// 경도 범위 체크 (-180 ~ 180)
if (lng < -180 || lng > 180) {
logger.warn(`⚠️ 경도 범위 초과: ${lng}`);
continue;
}
// 추가 정보 수집
const additionalInfo: Record<string, any> = {};
if (additionalColumns) {
for (const col of additionalColumns) {
if (col && row[col] !== undefined) {
additionalInfo[col] = row[col];
}
}
}
// 마커 생성
markers.push({
id: row.id || row.ID || `marker-${i}`,
latitude: lat,
longitude: lng,
label: labelColumn ? row[labelColumn] : undefined,
status: statusColumn ? row[statusColumn] : undefined,
additionalInfo: Object.keys(additionalInfo).length > 0 ? additionalInfo : undefined,
});
}
return markers;
}
/**
*
*/
private extractCoordinate(row: any, columnName: string): number | null {
const value = row[columnName];
if (value === null || value === undefined) {
return null;
}
// 이미 숫자인 경우
if (typeof value === "number") {
return value;
}
// 문자열인 경우 파싱
if (typeof value === "string") {
const parsed = parseFloat(value);
return isNaN(parsed) ? null : parsed;
}
return null;
}
}