[agent-pipeline] rollback to 577e9c12
This commit is contained in:
parent
27efe672b9
commit
0f15644651
|
|
@ -7,7 +7,6 @@ import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { ArrowLeft, Save, RefreshCw, ArrowRight, Trash2 } from "lucide-react";
|
import { ArrowLeft, Save, RefreshCw, ArrowRight, Trash2 } from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
@ -19,7 +18,6 @@ import {
|
||||||
ConnectionInfo,
|
ConnectionInfo,
|
||||||
ColumnInfo,
|
ColumnInfo,
|
||||||
BatchMappingRequest,
|
BatchMappingRequest,
|
||||||
NodeFlowItem,
|
|
||||||
} from "@/lib/api/batch";
|
} from "@/lib/api/batch";
|
||||||
|
|
||||||
export default function BatchCreatePage() {
|
export default function BatchCreatePage() {
|
||||||
|
|
@ -45,11 +43,6 @@ export default function BatchCreatePage() {
|
||||||
const [selectedFromColumn, setSelectedFromColumn] = useState<ColumnInfo | null>(null);
|
const [selectedFromColumn, setSelectedFromColumn] = useState<ColumnInfo | null>(null);
|
||||||
const [mappings, setMappings] = useState<BatchMapping[]>([]);
|
const [mappings, setMappings] = useState<BatchMapping[]>([]);
|
||||||
|
|
||||||
// 실행 타입: 데이터 매핑 / 노드 플로우
|
|
||||||
const [executionType, setExecutionType] = useState<"mapping" | "node_flow">("mapping");
|
|
||||||
const [selectedFlowId, setSelectedFlowId] = useState<number | null>(null);
|
|
||||||
const [nodeFlows, setNodeFlows] = useState<NodeFlowItem[]>([]);
|
|
||||||
|
|
||||||
// 로딩 상태
|
// 로딩 상태
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [loadingConnections, setLoadingConnections] = useState(false);
|
const [loadingConnections, setLoadingConnections] = useState(false);
|
||||||
|
|
@ -59,17 +52,6 @@ export default function BatchCreatePage() {
|
||||||
loadConnections();
|
loadConnections();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 노드 플로우 목록 로드 (실행 타입 노드 플로우 시 사용)
|
|
||||||
useEffect(() => {
|
|
||||||
if (executionType !== "node_flow") return;
|
|
||||||
const load = async () => {
|
|
||||||
const res = await BatchAPI.getNodeFlows();
|
|
||||||
if (res.success && res.data) setNodeFlows(res.data);
|
|
||||||
else setNodeFlows([]);
|
|
||||||
};
|
|
||||||
load();
|
|
||||||
}, [executionType]);
|
|
||||||
|
|
||||||
const loadConnections = async () => {
|
const loadConnections = async () => {
|
||||||
setLoadingConnections(true);
|
setLoadingConnections(true);
|
||||||
try {
|
try {
|
||||||
|
|
@ -239,28 +221,19 @@ export default function BatchCreatePage() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (executionType === "mapping") {
|
if (mappings.length === 0) {
|
||||||
if (mappings.length === 0) {
|
toast.error("최소 하나 이상의 매핑을 추가해주세요.");
|
||||||
toast.error("최소 하나 이상의 매핑을 추가해주세요.");
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (selectedFlowId == null) {
|
|
||||||
toast.error("노드 플로우를 선택해주세요.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const request: BatchMappingRequest = {
|
const request = {
|
||||||
batchName: batchName,
|
batchName: batchName,
|
||||||
description: description || undefined,
|
description: description || undefined,
|
||||||
cronSchedule: cronSchedule,
|
cronSchedule: cronSchedule,
|
||||||
mappings: executionType === "mapping" ? mappings : [],
|
mappings: mappings,
|
||||||
isActive: true,
|
isActive: true
|
||||||
executionType,
|
|
||||||
nodeFlowId: executionType === "node_flow" ? selectedFlowId ?? undefined : undefined,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
await BatchAPI.createBatchConfig(request);
|
await BatchAPI.createBatchConfig(request);
|
||||||
|
|
@ -332,66 +305,10 @@ export default function BatchCreatePage() {
|
||||||
rows={3}
|
rows={3}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 실행 타입 */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<Label>실행 타입</Label>
|
|
||||||
<RadioGroup
|
|
||||||
value={executionType}
|
|
||||||
onValueChange={(v) => {
|
|
||||||
setExecutionType(v as "mapping" | "node_flow");
|
|
||||||
if (v === "mapping") setSelectedFlowId(null);
|
|
||||||
}}
|
|
||||||
className="flex flex-col gap-2 sm:flex-row sm:gap-6"
|
|
||||||
>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<RadioGroupItem value="mapping" id="exec-mapping" />
|
|
||||||
<Label htmlFor="exec-mapping" className="font-normal cursor-pointer">
|
|
||||||
데이터 매핑 (기본)
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<RadioGroupItem value="node_flow" id="exec-node-flow" />
|
|
||||||
<Label htmlFor="exec-node-flow" className="font-normal cursor-pointer">
|
|
||||||
노드 플로우
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
</RadioGroup>
|
|
||||||
{executionType === "node_flow" && (
|
|
||||||
<div className="space-y-2 pt-2">
|
|
||||||
<Label>노드 플로우 선택</Label>
|
|
||||||
<Select
|
|
||||||
value={selectedFlowId != null ? String(selectedFlowId) : ""}
|
|
||||||
onValueChange={(v) => setSelectedFlowId(v ? parseInt(v, 10) : null)}
|
|
||||||
disabled={nodeFlows.length === 0}
|
|
||||||
>
|
|
||||||
<SelectTrigger className="w-full max-w-md">
|
|
||||||
<SelectValue placeholder={nodeFlows.length === 0 ? "로딩 중..." : "플로우를 선택하세요"} />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{nodeFlows.map((flow) => (
|
|
||||||
<SelectItem key={flow.flow_id} value={String(flow.flow_id)}>
|
|
||||||
<span className="font-medium">{flow.flow_name}</span>
|
|
||||||
{flow.description && (
|
|
||||||
<span className="ml-2 text-muted-foreground text-xs">({flow.description})</span>
|
|
||||||
)}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
{selectedFlowId != null && nodeFlows.find((f) => f.flow_id === selectedFlowId)?.description && (
|
|
||||||
<p className="text-xs text-muted-foreground">
|
|
||||||
{nodeFlows.find((f) => f.flow_id === selectedFlowId)?.description}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* 매핑 설정 - 데이터 매핑일 때만 표시 */}
|
{/* 매핑 설정 */}
|
||||||
{executionType === "mapping" && (
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||||
{/* FROM 섹션 */}
|
{/* FROM 섹션 */}
|
||||||
<Card className="border-emerald-200">
|
<Card className="border-emerald-200">
|
||||||
|
|
@ -557,10 +474,9 @@ export default function BatchCreatePage() {
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 매핑 현황 */}
|
{/* 매핑 현황 */}
|
||||||
{executionType === "mapping" && mappings.length > 0 && (
|
{mappings.length > 0 && (
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>컬럼 매핑 현황 ({mappings.length}개)</CardTitle>
|
<CardTitle>컬럼 매핑 현황 ({mappings.length}개)</CardTitle>
|
||||||
|
|
@ -613,7 +529,7 @@ export default function BatchCreatePage() {
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={saveBatchConfig}
|
onClick={saveBatchConfig}
|
||||||
disabled={loading || (executionType === "mapping" ? mappings.length === 0 : selectedFlowId == null)}
|
disabled={loading || mappings.length === 0}
|
||||||
className="flex items-center space-x-2"
|
className="flex items-center space-x-2"
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ import {
|
||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { RefreshCw, Save, ArrowLeft, Plus, Trash2 } from "lucide-react";
|
import { RefreshCw, Save, ArrowLeft, Plus, Trash2 } from "lucide-react";
|
||||||
|
|
@ -24,7 +23,6 @@ import {
|
||||||
BatchConfig,
|
BatchConfig,
|
||||||
BatchMapping,
|
BatchMapping,
|
||||||
ConnectionInfo,
|
ConnectionInfo,
|
||||||
NodeFlowItem,
|
|
||||||
} from "@/lib/api/batch";
|
} from "@/lib/api/batch";
|
||||||
import { BatchManagementAPI } from "@/lib/api/batchManagement";
|
import { BatchManagementAPI } from "@/lib/api/batchManagement";
|
||||||
|
|
||||||
|
|
@ -66,11 +64,6 @@ export default function BatchEditPage() {
|
||||||
const [authServiceNames, setAuthServiceNames] = useState<string[]>([]);
|
const [authServiceNames, setAuthServiceNames] = useState<string[]>([]);
|
||||||
const [dataArrayPath, setDataArrayPath] = useState("");
|
const [dataArrayPath, setDataArrayPath] = useState("");
|
||||||
|
|
||||||
// 실행 타입: 데이터 매핑 / 노드 플로우
|
|
||||||
const [executionType, setExecutionType] = useState<"mapping" | "node_flow">("mapping");
|
|
||||||
const [selectedFlowId, setSelectedFlowId] = useState<number | null>(null);
|
|
||||||
const [nodeFlows, setNodeFlows] = useState<NodeFlowItem[]>([]);
|
|
||||||
|
|
||||||
// 연결 정보
|
// 연결 정보
|
||||||
const [connections, setConnections] = useState<ConnectionInfo[]>([]);
|
const [connections, setConnections] = useState<ConnectionInfo[]>([]);
|
||||||
const [fromConnection, setFromConnection] = useState<ConnectionInfo | null>(null);
|
const [fromConnection, setFromConnection] = useState<ConnectionInfo | null>(null);
|
||||||
|
|
@ -123,17 +116,6 @@ export default function BatchEditPage() {
|
||||||
}
|
}
|
||||||
}, [batchId]);
|
}, [batchId]);
|
||||||
|
|
||||||
// 노드 플로우 목록 로드 (실행 타입 노드 플로우 시 사용)
|
|
||||||
useEffect(() => {
|
|
||||||
if (executionType !== "node_flow") return;
|
|
||||||
const load = async () => {
|
|
||||||
const res = await BatchAPI.getNodeFlows();
|
|
||||||
if (res.success && res.data) setNodeFlows(res.data);
|
|
||||||
else setNodeFlows([]);
|
|
||||||
};
|
|
||||||
load();
|
|
||||||
}, [executionType]);
|
|
||||||
|
|
||||||
// 인증 서비스명 목록 로드
|
// 인증 서비스명 목록 로드
|
||||||
const loadAuthServiceNames = async () => {
|
const loadAuthServiceNames = async () => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -242,8 +224,6 @@ export default function BatchEditPage() {
|
||||||
setConflictKey((config as any).conflict_key || "");
|
setConflictKey((config as any).conflict_key || "");
|
||||||
setAuthServiceName((config as any).auth_service_name || "");
|
setAuthServiceName((config as any).auth_service_name || "");
|
||||||
setDataArrayPath((config as any).data_array_path || "");
|
setDataArrayPath((config as any).data_array_path || "");
|
||||||
setExecutionType((config.execution_type as "mapping" | "node_flow") || "mapping");
|
|
||||||
setSelectedFlowId(config.node_flow_id ?? null);
|
|
||||||
|
|
||||||
// 인증 토큰 모드 설정
|
// 인증 토큰 모드 설정
|
||||||
if ((config as any).auth_service_name) {
|
if ((config as any).auth_service_name) {
|
||||||
|
|
@ -559,33 +539,21 @@ export default function BatchEditPage() {
|
||||||
|
|
||||||
// 배치 설정 저장
|
// 배치 설정 저장
|
||||||
const saveBatchConfig = async () => {
|
const saveBatchConfig = async () => {
|
||||||
if (!batchName || !cronSchedule) {
|
// restapi-to-db인 경우 mappingList 사용, 아닌 경우 mappings 사용
|
||||||
|
const effectiveMappings = batchType === "restapi-to-db" ? mappingList : mappings;
|
||||||
|
|
||||||
|
if (!batchName || !cronSchedule || effectiveMappings.length === 0) {
|
||||||
toast.error("필수 항목을 모두 입력해주세요.");
|
toast.error("필수 항목을 모두 입력해주세요.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (executionType === "node_flow") {
|
|
||||||
if (selectedFlowId == null) {
|
|
||||||
toast.error("노드 플로우를 선택해주세요.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const effectiveMappings = batchType === "restapi-to-db" ? mappingList : mappings;
|
|
||||||
if (effectiveMappings.length === 0) {
|
|
||||||
toast.error("필수 항목을 모두 입력해주세요.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const effectiveMappings = batchType === "restapi-to-db" ? mappingList : mappings;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
// 노드 플로우 타입이면 매핑 없이 전송
|
// restapi-to-db인 경우 mappingList를 mappings 형식으로 변환
|
||||||
let finalMappings: BatchMapping[] =
|
let finalMappings: BatchMapping[] = mappings;
|
||||||
executionType === "node_flow" ? [] : mappings;
|
|
||||||
|
|
||||||
if (executionType !== "node_flow" && batchType === "restapi-to-db" && batchConfig?.batch_mappings?.[0]) {
|
if (batchType === "restapi-to-db" && batchConfig?.batch_mappings?.[0]) {
|
||||||
const first = batchConfig.batch_mappings[0] as any;
|
const first = batchConfig.batch_mappings[0] as any;
|
||||||
finalMappings = mappingList
|
finalMappings = mappingList
|
||||||
.filter((m) => m.dbColumn) // DB 컬럼이 선택된 것만
|
.filter((m) => m.dbColumn) // DB 컬럼이 선택된 것만
|
||||||
|
|
@ -618,11 +586,9 @@ export default function BatchEditPage() {
|
||||||
isActive,
|
isActive,
|
||||||
mappings: finalMappings,
|
mappings: finalMappings,
|
||||||
saveMode,
|
saveMode,
|
||||||
conflictKey: saveMode === "UPSERT" ? conflictKey : null,
|
conflictKey: saveMode === "UPSERT" ? conflictKey : null, // INSERT면 null로 명시적 삭제
|
||||||
authServiceName: authTokenMode === "db" ? authServiceName : null,
|
authServiceName: authTokenMode === "db" ? authServiceName : null, // 직접입력이면 null로 명시적 삭제
|
||||||
dataArrayPath: dataArrayPath || null,
|
dataArrayPath: dataArrayPath || null
|
||||||
executionType,
|
|
||||||
nodeFlowId: executionType === "node_flow" ? selectedFlowId ?? undefined : undefined,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
toast.success("배치 설정이 성공적으로 수정되었습니다.");
|
toast.success("배치 설정이 성공적으로 수정되었습니다.");
|
||||||
|
|
@ -717,61 +683,6 @@ export default function BatchEditPage() {
|
||||||
/>
|
/>
|
||||||
<Label htmlFor="isActive">활성화</Label>
|
<Label htmlFor="isActive">활성화</Label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 실행 타입 */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<Label>실행 타입</Label>
|
|
||||||
<RadioGroup
|
|
||||||
value={executionType}
|
|
||||||
onValueChange={(v) => {
|
|
||||||
setExecutionType(v as "mapping" | "node_flow");
|
|
||||||
if (v === "mapping") setSelectedFlowId(null);
|
|
||||||
}}
|
|
||||||
className="flex flex-col gap-2 sm:flex-row sm:gap-6"
|
|
||||||
>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<RadioGroupItem value="mapping" id="edit-exec-mapping" />
|
|
||||||
<Label htmlFor="edit-exec-mapping" className="font-normal cursor-pointer">
|
|
||||||
데이터 매핑 (기본)
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<RadioGroupItem value="node_flow" id="edit-exec-node-flow" />
|
|
||||||
<Label htmlFor="edit-exec-node-flow" className="font-normal cursor-pointer">
|
|
||||||
노드 플로우
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
</RadioGroup>
|
|
||||||
{executionType === "node_flow" && (
|
|
||||||
<div className="space-y-2 pt-2">
|
|
||||||
<Label>노드 플로우 선택</Label>
|
|
||||||
<Select
|
|
||||||
value={selectedFlowId != null ? String(selectedFlowId) : ""}
|
|
||||||
onValueChange={(v) => setSelectedFlowId(v ? parseInt(v, 10) : null)}
|
|
||||||
disabled={nodeFlows.length === 0}
|
|
||||||
>
|
|
||||||
<SelectTrigger className="w-full max-w-md">
|
|
||||||
<SelectValue placeholder={nodeFlows.length === 0 ? "로딩 중..." : "플로우를 선택하세요"} />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{nodeFlows.map((flow) => (
|
|
||||||
<SelectItem key={flow.flow_id} value={String(flow.flow_id)}>
|
|
||||||
<span className="font-medium">{flow.flow_name}</span>
|
|
||||||
{flow.description != null && flow.description !== "" && (
|
|
||||||
<span className="ml-2 text-muted-foreground text-xs">({flow.description})</span>
|
|
||||||
)}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
{selectedFlowId != null && nodeFlows.find((f) => f.flow_id === selectedFlowId)?.description && (
|
|
||||||
<p className="text-xs text-muted-foreground">
|
|
||||||
{nodeFlows.find((f) => f.flow_id === selectedFlowId)?.description}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|
@ -1625,14 +1536,7 @@ export default function BatchEditPage() {
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={saveBatchConfig}
|
onClick={saveBatchConfig}
|
||||||
disabled={
|
disabled={loading || (batchType === "restapi-to-db" ? mappingList.length === 0 : mappings.length === 0)}
|
||||||
loading ||
|
|
||||||
(executionType === "node_flow"
|
|
||||||
? selectedFlowId == null
|
|
||||||
: batchType === "restapi-to-db"
|
|
||||||
? mappingList.length === 0
|
|
||||||
: mappings.length === 0)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<RefreshCw className="mr-2 h-4 w-4 animate-spin" />
|
<RefreshCw className="mr-2 h-4 w-4 animate-spin" />
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,6 @@ export interface BatchConfig {
|
||||||
save_mode?: 'INSERT' | 'UPSERT'; // 저장 모드 (기본: INSERT)
|
save_mode?: 'INSERT' | 'UPSERT'; // 저장 모드 (기본: INSERT)
|
||||||
conflict_key?: string; // UPSERT 시 충돌 기준 컬럼명
|
conflict_key?: string; // UPSERT 시 충돌 기준 컬럼명
|
||||||
auth_service_name?: string; // REST API 인증에 사용할 토큰 서비스명
|
auth_service_name?: string; // REST API 인증에 사용할 토큰 서비스명
|
||||||
execution_type?: 'mapping' | 'node_flow';
|
|
||||||
node_flow_id?: number;
|
|
||||||
node_flow_context?: Record<string, unknown>;
|
|
||||||
created_date?: Date;
|
created_date?: Date;
|
||||||
created_by?: string;
|
created_by?: string;
|
||||||
updated_date?: Date;
|
updated_date?: Date;
|
||||||
|
|
@ -98,17 +95,6 @@ export interface BatchMappingRequest {
|
||||||
cronSchedule: string;
|
cronSchedule: string;
|
||||||
mappings: BatchMapping[];
|
mappings: BatchMapping[];
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
executionType?: 'mapping' | 'node_flow';
|
|
||||||
nodeFlowId?: number;
|
|
||||||
nodeFlowContext?: Record<string, unknown>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 노드 플로우 목록 항목 (getNodeFlows 응답) */
|
|
||||||
export interface NodeFlowItem {
|
|
||||||
flow_id: number;
|
|
||||||
flow_name: string;
|
|
||||||
description?: string;
|
|
||||||
created_date?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiResponse<T> {
|
export interface ApiResponse<T> {
|
||||||
|
|
@ -474,97 +460,6 @@ export class BatchAPI {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 노드 플로우 목록 조회
|
|
||||||
*/
|
|
||||||
static async getNodeFlows(): Promise<ApiResponse<NodeFlowItem[]>> {
|
|
||||||
try {
|
|
||||||
const response = await apiClient.get<ApiResponse<NodeFlowItem[]>>(
|
|
||||||
"/batch-management/node-flows"
|
|
||||||
);
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("노드 플로우 목록 조회 오류:", error);
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: error instanceof Error ? error.message : "노드 플로우 목록 조회에 실패했습니다.",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 배치 통계 (대시보드용)
|
|
||||||
*/
|
|
||||||
static async getBatchStats(): Promise<
|
|
||||||
ApiResponse<{
|
|
||||||
totalBatches: number;
|
|
||||||
activeBatches: number;
|
|
||||||
todayExecutions: number;
|
|
||||||
todayFailures: number;
|
|
||||||
}>
|
|
||||||
> {
|
|
||||||
try {
|
|
||||||
const response = await apiClient.get<
|
|
||||||
ApiResponse<{
|
|
||||||
totalBatches: number;
|
|
||||||
activeBatches: number;
|
|
||||||
todayExecutions: number;
|
|
||||||
todayFailures: number;
|
|
||||||
}>
|
|
||||||
>("/batch-management/stats");
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("배치 통계 조회 오류:", error);
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: error instanceof Error ? error.message : "배치 통계 조회에 실패했습니다.",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 배치별 스파크라인 (최근 24시간 성공/실패 추이)
|
|
||||||
*/
|
|
||||||
static async getBatchSparkline(
|
|
||||||
batchId: number
|
|
||||||
): Promise<
|
|
||||||
ApiResponse<Array<{ hour: number; status: string; count: number }>>
|
|
||||||
> {
|
|
||||||
try {
|
|
||||||
const response = await apiClient.get<
|
|
||||||
ApiResponse<Array<{ hour: number; status: string; count: number }>>
|
|
||||||
>(`/batch-management/batch-configs/${batchId}/sparkline`);
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("배치 스파크라인 조회 오류:", error);
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: error instanceof Error ? error.message : "스파크라인 조회에 실패했습니다.",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 배치 최근 실행 이력
|
|
||||||
*/
|
|
||||||
static async getBatchRecentLogs(
|
|
||||||
batchId: number,
|
|
||||||
limit: number = 5
|
|
||||||
): Promise<ApiResponse<unknown[]>> {
|
|
||||||
try {
|
|
||||||
const response = await apiClient.get<ApiResponse<unknown[]>>(
|
|
||||||
`/batch-management/batch-configs/${batchId}/recent-logs?limit=${limit}`
|
|
||||||
);
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("배치 최근 로그 조회 오류:", error);
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: error instanceof Error ? error.message : "최근 로그 조회에 실패했습니다.",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BatchJob export 추가 (이미 위에서 interface로 정의됨)
|
// BatchJob export 추가 (이미 위에서 interface로 정의됨)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue