import axios from 'axios' const api = axios.create({ baseURL: 'http://localhost:8013' }) api.interceptors.request.use(config => { const token = localStorage.getItem('mes_token') if (token) config.headers.Authorization = `Bearer ${token}` return config }) api.interceptors.response.use( res => res, err => { if (err.response?.status === 401) { localStorage.removeItem('mes_token') if (location.pathname !== '/login') location.href = '/login' } return Promise.reject(err) } ) export default api // 공통 래퍼 {success,message,data} → data 추출 const unwrap = (p: Promise) => p.then(r => r.data?.data) const qs = (o: Record) => { const p = new URLSearchParams() Object.entries(o).forEach(([k, v]) => { if (v !== undefined && v !== null && v !== '') p.set(k, String(v)) }) const s = p.toString() return s ? `?${s}` : '' } // ── Auth ────────────────────────────────────────────────────────────── export const login = (username: string, password: string) => api.post('/api/mes/auth/login', { username, password }) export const getMe = () => api.get('/api/mes/auth/me') // ── Dashboard ───────────────────────────────────────────────────────── export const getDashboardSummary = () => unwrap(api.get('/api/mes/dashboard/summary')) export const getWorkorderStatus = () => unwrap(api.get('/api/mes/dashboard/workorder-status')) export const getShippingStatusCard = () => unwrap(api.get('/api/mes/dashboard/shipping-status')) export const getEquipmentStatusCard = () => unwrap(api.get('/api/mes/dashboard/equipment-status')) // ── ★ 작업관리 (job) ────────────────────────────────────────────────── export const getJobs = (f: { status?: string; workerId?: number | string; woNo?: string; equipmentCode?: string } = {}) => unwrap(api.get(`/api/mes/jobs${qs(f)}`)) export const getJobBoard = () => unwrap(api.get('/api/mes/jobs/board')) export const getJobWorkload = () => unwrap(api.get('/api/mes/jobs/workload')) export const getJob = (id: number) => unwrap(api.get(`/api/mes/jobs/${id}`)) export const createJob = (d: object) => unwrap(api.post('/api/mes/jobs', d)) export const updateJob = (id: number, d: object) => unwrap(api.put(`/api/mes/jobs/${id}`, d)) export const assignJob = (id: number, body: { workerId?: number | string; equipmentCode?: string }) => unwrap(api.post(`/api/mes/jobs/${id}/assign`, body)) export const transitionJob = (id: number, action: string) => unwrap(api.post(`/api/mes/jobs/${id}/transition`, { action })) export const reportJob = (id: number, qty: number) => unwrap(api.post(`/api/mes/jobs/${id}/report`, { qty })) export const deleteJob = (id: number) => api.delete(`/api/mes/jobs/${id}`) // ── ★ 진행관리 (progress) ───────────────────────────────────────────── export const getProgressSummary = () => unwrap(api.get('/api/mes/progress/summary')) export const getProgressWorkorders = (status = '') => unwrap(api.get(`/api/mes/progress/workorders${status ? `?status=${status}` : ''}`)) export const getProgressWorkorder = (id: number) => unwrap(api.get(`/api/mes/progress/workorders/${id}`)) export const getProgressLive = () => unwrap(api.get('/api/mes/progress/live')) export const getPlanVsActual = (days = 30) => unwrap(api.get(`/api/mes/progress/plan-vs-actual?days=${days}`)) export const getProgressDelays = () => unwrap(api.get('/api/mes/progress/delays')) // ── ★ 출하관리 (shipping) ───────────────────────────────────────────── export const getShippings = (f: { status?: string; partnerCode?: string; keyword?: string } = {}) => unwrap(api.get(`/api/mes/shipping${qs(f)}`)) export const getShippingBoard = () => unwrap(api.get('/api/mes/shipping/board')) export const getShipping = (id: number) => unwrap(api.get(`/api/mes/shipping/${id}`)) export const createShipping = (d: object) => unwrap(api.post('/api/mes/shipping', d)) export const updateShipping = (id: number, d: object) => unwrap(api.put(`/api/mes/shipping/${id}`, d)) export const pickShipping = (id: number, qty: number) => unwrap(api.post(`/api/mes/shipping/${id}/pick`, { qty })) export const packShipping = (id: number, qty: number) => unwrap(api.post(`/api/mes/shipping/${id}/pack`, { qty })) export const inspectShipping = (id: number, result: 'PASS' | 'FAIL') => unwrap(api.post(`/api/mes/shipping/${id}/inspect`, { result })) export const shipShipping = (id: number) => unwrap(api.post(`/api/mes/shipping/${id}/ship`)) export const cancelShipping = (id: number) => unwrap(api.post(`/api/mes/shipping/${id}/cancel`)) // ── MES 작업지시 (workorder) ────────────────────────────────────────── export const getWorkOrders = (f: { status?: string; itemCode?: string; keyword?: string } = {}) => unwrap(api.get(`/api/mes/workorders${qs(f)}`)) export const getWorkOrderKanban = () => unwrap(api.get('/api/mes/workorders/kanban')) export const getWorkOrder = (id: number) => unwrap(api.get(`/api/mes/workorders/${id}`)) export const createWorkOrder = (d: object) => unwrap(api.post('/api/mes/workorders', d)) export const updateWorkOrder = (id: number, d: object) => unwrap(api.put(`/api/mes/workorders/${id}`, d)) export const transitionWorkOrder = (id: number, action: string) => unwrap(api.post(`/api/mes/workorders/${id}/transition`, { action })) export const deleteWorkOrder = (id: number) => api.delete(`/api/mes/workorders/${id}`) // ── MES 생산실적 (production) ───────────────────────────────────────── export const getProductions = (f: { woNo?: string; itemCode?: string; limit?: number } = {}) => unwrap(api.get(`/api/mes/production${qs(f)}`)) export const getProduction = (id: number) => unwrap(api.get(`/api/mes/production/${id}`)) export const getDailyOutput = (days = 14) => unwrap(api.get(`/api/mes/production/daily-output?days=${days}`)) export const getDefectPareto = (days = 30) => unwrap(api.get(`/api/mes/production/defect-pareto?days=${days}`)) export const reportProduction = (d: object) => unwrap(api.post('/api/mes/production/report', d)) // ── MES 공정진행 (process) ──────────────────────────────────────────── export const getProcessesByWo = (workorderId: number) => unwrap(api.get(`/api/mes/process/workorder/${workorderId}`)) export const getRunningProcesses = () => unwrap(api.get('/api/mes/process/running')) export const createProcess = (d: object) => unwrap(api.post('/api/mes/process', d)) export const updateProcess = (id: number, d: object) => unwrap(api.put(`/api/mes/process/${id}`, d)) export const setProcessStatus = (id: number, status: string) => unwrap(api.put(`/api/mes/process/${id}/status`, { status })) // ── MES OEE (oee) ───────────────────────────────────────────────────── export const getOees = (f: { equipmentCode?: string; limit?: number } = {}) => unwrap(api.get(`/api/mes/oee${qs(f)}`)) export const createOee = (d: object) => unwrap(api.post('/api/mes/oee', d)) export const computeOee = (d: object) => unwrap(api.post('/api/mes/oee/compute', d)) export const getOeeDetail = (id: number) => unwrap(api.get(`/api/mes/oee/${id}/oee`)) export const getDowntimePareto = (days = 30) => unwrap(api.get(`/api/mes/oee/downtime-pareto?days=${days}`)) // ── MES LOT 추적 (tracking) ─────────────────────────────────────────── export const getLotHistory = (lotNo: string) => unwrap(api.get(`/api/mes/tracking/lot/${encodeURIComponent(lotNo)}/history`)) export const getLotForward = (lotNo: string) => unwrap(api.get(`/api/mes/tracking/lot/${encodeURIComponent(lotNo)}/forward`)) export const getLotBackward = (lotNo: string) => unwrap(api.get(`/api/mes/tracking/lot/${encodeURIComponent(lotNo)}/backward`)) // ── MES 생산계획 (plan) ─────────────────────────────────────────────── export const getPlans = (f: { status?: string; itemCode?: string } = {}) => unwrap(api.get(`/api/mes/plans${qs(f)}`)) export const getPlan = (id: number) => unwrap(api.get(`/api/mes/plans/${id}`)) export const createPlan = (d: object) => unwrap(api.post('/api/mes/plans', d)) export const updatePlan = (id: number, d: object) => unwrap(api.put(`/api/mes/plans/${id}`, d)) export const transitionPlan = (id: number, action: string) => unwrap(api.post(`/api/mes/plans/${id}/transition`, { action })) export const deletePlan = (id: number) => api.delete(`/api/mes/plans/${id}`) // ── WMS 입고 (receiving) ────────────────────────────────────────────── export const getReceivings = (f: { status?: string; itemCode?: string } = {}) => unwrap(api.get(`/api/mes/receiving${qs(f)}`)) export const getReceiving = (id: number) => unwrap(api.get(`/api/mes/receiving/${id}`)) export const createReceiving = (d: object) => unwrap(api.post('/api/mes/receiving', d)) export const acceptReceiving = (id: number, inspectionResult?: string) => unwrap(api.post(`/api/mes/receiving/${id}/accept`, { inspectionResult })) export const rejectReceiving = (id: number, reason?: string) => unwrap(api.post(`/api/mes/receiving/${id}/reject`, { reason })) // ── WMS 재고 (inventory) ────────────────────────────────────────────── export const getInventory = (f: { itemCode?: string; warehouseCode?: string; keyword?: string } = {}) => unwrap(api.get(`/api/mes/inventory${qs(f)}`)) export const getAvailable = (itemCode: string) => unwrap(api.get(`/api/mes/inventory/available/${encodeURIComponent(itemCode)}`)) export const getBelowSafety = () => unwrap(api.get('/api/mes/inventory/below-safety')) export const getInventoryTxns = (f: { itemCode?: string; refType?: string; refNo?: string; limit?: number } = {}) => unwrap(api.get(`/api/mes/inventory/txns${qs(f)}`)) export const transferInventory = (d: object) => unwrap(api.post('/api/mes/inventory/transfer', d)) // ── WMS 재고실사 (stocktake) ────────────────────────────────────────── export const getStocktakes = (f: { stocktakeNo?: string; status?: string } = {}) => unwrap(api.get(`/api/mes/stocktake${qs(f)}`)) export const getStocktake = (id: number) => unwrap(api.get(`/api/mes/stocktake/${id}`)) export const countStocktake = (d: object) => unwrap(api.post('/api/mes/stocktake/count', d)) export const adjustStocktake = (id: number) => unwrap(api.post(`/api/mes/stocktake/${id}/adjust`)) // ── WMS LOT/시리얼 (lotserial) ──────────────────────────────────────── export const getLotSerials = (f: { itemCode?: string; status?: string; keyword?: string } = {}) => unwrap(api.get(`/api/mes/lotserial${qs(f)}`)) export const getLotSerial = (id: number) => unwrap(api.get(`/api/mes/lotserial/${id}`)) export const getExpiringLots = (days = 30) => unwrap(api.get(`/api/mes/lotserial/expiring?days=${days}`)) export const createLotSerial = (d: object) => unwrap(api.post('/api/mes/lotserial', d)) export const setLotSerialStatus = (id: number, status: string) => unwrap(api.put(`/api/mes/lotserial/${id}/status`, { status })) // ── QMS 검사 (inspection) ───────────────────────────────────────────── export const getInspections = (f: { inspectionType?: string; result?: string; itemCode?: string } = {}) => unwrap(api.get(`/api/mes/inspections${qs(f)}`)) export const getInspectionPassRate = (days = 30) => unwrap(api.get(`/api/mes/inspections/pass-rate?days=${days}`)) export const getInspection = (id: number) => unwrap(api.get(`/api/mes/inspections/${id}`)) export const createInspection = (d: object) => unwrap(api.post('/api/mes/inspections', d)) export const judgeInspection = (id: number, d: object) => unwrap(api.post(`/api/mes/inspections/${id}/judge`, d)) export const resultInspection = (id: number, d: object) => unwrap(api.post(`/api/mes/inspections/${id}/result`, d)) // ── QMS 검사기준 (spec) ─────────────────────────────────────────────── export const getSpecs = (f: { itemCode?: string; inspectionType?: string } = {}) => unwrap(api.get(`/api/mes/specs${qs(f)}`)) export const getSpec = (id: number) => unwrap(api.get(`/api/mes/specs/${id}`)) export const createSpec = (d: object) => unwrap(api.post('/api/mes/specs', d)) export const updateSpec = (id: number, d: object) => unwrap(api.put(`/api/mes/specs/${id}`, d)) export const deleteSpec = (id: number) => api.delete(`/api/mes/specs/${id}`) // ── QMS 부적합 (ncr) ────────────────────────────────────────────────── export const getNcrs = (f: { status?: string; severity?: string; itemCode?: string } = {}) => unwrap(api.get(`/api/mes/ncr${qs(f)}`)) export const getNcrBoard = () => unwrap(api.get('/api/mes/ncr/board')) export const getNcr = (id: number) => unwrap(api.get(`/api/mes/ncr/${id}`)) export const createNcr = (d: object) => unwrap(api.post('/api/mes/ncr', d)) export const dispositionNcr = (id: number, disposition: string) => unwrap(api.post(`/api/mes/ncr/${id}/disposition`, { disposition })) export const transitionNcr = (id: number, status: string) => unwrap(api.post(`/api/mes/ncr/${id}/transition`, { status })) export const linkNcrCapa = (id: number, capaId: number) => unwrap(api.post(`/api/mes/ncr/${id}/link-capa`, { capaId })) // ── QMS 시정조치 (capa) ─────────────────────────────────────────────── export const getCapas = (f: { status?: string; capaType?: string } = {}) => unwrap(api.get(`/api/mes/capa${qs(f)}`)) export const getCapa = (id: number) => unwrap(api.get(`/api/mes/capa/${id}`)) export const createCapa = (d: object) => unwrap(api.post('/api/mes/capa', d)) export const updateCapa = (id: number, d: object) => unwrap(api.put(`/api/mes/capa/${id}`, d)) export const transitionCapa = (id: number, body: { status: string; effectiveness?: string }) => unwrap(api.post(`/api/mes/capa/${id}/transition`, body)) // ── QMS SPC 관리도 (spc) ────────────────────────────────────────────── export const getSpcCharts = () => unwrap(api.get('/api/mes/spc/charts')) export const getSpcSamples = (f: { chartCode?: string; limit?: number } = {}) => unwrap(api.get(`/api/mes/spc/samples${qs(f)}`)) export const getSpcChart = (f: { chartCode: string; lsl?: number; usl?: number }) => unwrap(api.get(`/api/mes/spc/chart${qs(f)}`)) export const addSpcSample = (d: object) => unwrap(api.post('/api/mes/spc/samples', d)) // ── QMS 성적서 (certificate) ────────────────────────────────────────── export const getCertificates = (f: { certType?: string; itemCode?: string; lotNo?: string } = {}) => unwrap(api.get(`/api/mes/certificates${qs(f)}`)) export const getCertificate = (id: number) => unwrap(api.get(`/api/mes/certificates/${id}`)) export const createCertificate = (d: object) => unwrap(api.post('/api/mes/certificates', d)) // ── 기준정보 — 품목 (items) ─────────────────────────────────────────── export const getItems = (f: { itemType?: string; status?: string; keyword?: string } = {}) => unwrap(api.get(`/api/mes/items${qs(f)}`)) export const getItem = (id: number) => unwrap(api.get(`/api/mes/items/${id}`)) export const createItem = (d: object) => unwrap(api.post('/api/mes/items', d)) export const updateItem = (id: number, d: object) => unwrap(api.put(`/api/mes/items/${id}`, d)) export const deleteItem = (id: number) => api.delete(`/api/mes/items/${id}`) // ── 기준정보 — BOM ──────────────────────────────────────────────────── export const getBomTree = (f: { parentItemCode?: string; bomVersion?: string } = {}) => unwrap(api.get(`/api/mes/bom${qs(f)}`)) export const getAllBom = () => unwrap(api.get('/api/mes/bom/all')) export const createBom = (d: object) => unwrap(api.post('/api/mes/bom', d)) export const updateBom = (id: number, d: object) => unwrap(api.put(`/api/mes/bom/${id}`, d)) export const deleteBom = (id: number) => api.delete(`/api/mes/bom/${id}`) // ── 기준정보 — 라우팅 (routings) ────────────────────────────────────── export const getRoutings = (itemCode = '') => unwrap(api.get(`/api/mes/routings${itemCode ? `?itemCode=${encodeURIComponent(itemCode)}` : ''}`)) export const createRouting = (d: object) => unwrap(api.post('/api/mes/routings', d)) export const updateRouting = (id: number, d: object) => unwrap(api.put(`/api/mes/routings/${id}`, d)) export const deleteRouting = (id: number) => api.delete(`/api/mes/routings/${id}`) // ── 기준정보 — 설비 (equipment) ─────────────────────────────────────── export const getEquipment = (f: { status?: string; runStatus?: string } = {}) => unwrap(api.get(`/api/mes/equipment${qs(f)}`)) export const createEquipment = (d: object) => unwrap(api.post('/api/mes/equipment', d)) export const updateEquipment = (id: number, d: object) => unwrap(api.put(`/api/mes/equipment/${id}`, d)) export const setEquipmentRunStatus = (id: number, runStatus: string) => unwrap(api.put(`/api/mes/equipment/${id}/run-status`, { runStatus })) export const deleteEquipment = (id: number) => api.delete(`/api/mes/equipment/${id}`) // ── 기준정보 — 거래처 (partners) ────────────────────────────────────── export const getPartners = (f: { partnerType?: string; status?: string; keyword?: string } = {}) => unwrap(api.get(`/api/mes/partners${qs(f)}`)) export const createPartner = (d: object) => unwrap(api.post('/api/mes/partners', d)) export const updatePartner = (id: number, d: object) => unwrap(api.put(`/api/mes/partners/${id}`, d)) export const deletePartner = (id: number) => api.delete(`/api/mes/partners/${id}`) // ── 기준정보 — 창고/로케이션 (warehouses) ───────────────────────────── export const getWarehouses = () => unwrap(api.get('/api/mes/warehouses')) export const createWarehouse = (d: object) => unwrap(api.post('/api/mes/warehouses', d)) export const updateWarehouse = (id: number, d: object) => unwrap(api.put(`/api/mes/warehouses/${id}`, d)) export const deleteWarehouse = (id: number) => api.delete(`/api/mes/warehouses/${id}`) export const getLocations = (warehouseCode: string) => unwrap(api.get(`/api/mes/warehouses/${encodeURIComponent(warehouseCode)}/locations`)) export const createLocation = (d: object) => unwrap(api.post('/api/mes/warehouses/locations', d)) export const deleteLocation = (id: number) => api.delete(`/api/mes/warehouses/locations/${id}`) // ── 구성원 (members) ────────────────────────────────────────────────── export const getMembers = (role = '') => unwrap(api.get(`/api/mes/members${role ? `?role=${role}` : ''}`)) export const getWorkers = () => unwrap(api.get('/api/mes/members/workers')) // ── 바코드 (barcode) ────────────────────────────────────────────────── export const generateBarcode = (type: string, value: string) => unwrap(api.post('/api/mes/barcode/generate', { type, value })) export const resolveBarcode = (code: string) => unwrap(api.get(`/api/mes/barcode/resolve?code=${encodeURIComponent(code)}`)) // ── 분석/KPI (analytics) ────────────────────────────────────────────── export const getProductionKpi = (days = 30) => unwrap(api.get(`/api/mes/analytics/production-kpi?days=${days}`)) export const getQualityKpi = (days = 30) => unwrap(api.get(`/api/mes/analytics/quality-kpi?days=${days}`)) export const getInventoryKpi = () => unwrap(api.get('/api/mes/analytics/inventory-kpi')) export const getDailyProduction = (days = 14) => unwrap(api.get(`/api/mes/analytics/daily-production?days=${days}`)) export const getTopItems = (days = 30, limit = 10) => unwrap(api.get(`/api/mes/analytics/top-items?days=${days}&limit=${limit}`)) export const getBiFeed = (days = 30) => unwrap(api.get(`/api/mes/analytics/bi-feed?days=${days}`)) // ── 연계 (integration) ──────────────────────────────────────────────── export const getIntegrationStatus = () => unwrap(api.get('/api/mes/integration/status')) export const getErpPlans = () => unwrap(api.get('/api/mes/integration/erp/plans')) export const getOcrDoc = (docId: string) => unwrap(api.get(`/api/mes/integration/ocr/${encodeURIComponent(docId)}`)) // ── AI (Ollama 폴백, 항상 200) ──────────────────────────────────────── export const getAiStatus = () => unwrap(api.get('/api/mes/ai/status')) export const aiDefectRootCause = (d: object) => unwrap(api.post('/api/mes/ai/defect-root-cause', d)) export const aiForecast = (d: object) => unwrap(api.post('/api/mes/ai/forecast', d)) export const aiPredictiveMaintenance = (d: object) => unwrap(api.post('/api/mes/ai/predictive-maintenance', d)) export const aiSpcAnomaly = (d: object) => unwrap(api.post('/api/mes/ai/spc-anomaly', d)) export const aiParseQuery = (d: object) => unwrap(api.post('/api/mes/ai/parse-query', d)) export const aiInspectionJudge = (d: object) => unwrap(api.post('/api/mes/ai/inspection-judge', d)) export const aiSafetyStock = (d: object) => unwrap(api.post('/api/mes/ai/safety-stock', d)) export const aiScheduleOptimize = (d: object) => unwrap(api.post('/api/mes/ai/schedule-optimize', d)) // ── RAG 최신 AI 기법 (중앙 guardia-rag 경유, 별개 레이어 — 항상 200, 폴백 degraded) ── // 불량 RCA + SPC 이상감지: SPC 수치는 결정론, 원인 서술은 /rag/agent(+structured) export const ragDefectAnalysis = (d: object) => unwrap(api.post('/api/mes/rag/defect-analysis', d)) // 설비 예지보전 + 수요/생산 예측: 수치는 결정론 베이스라인, 해석은 /rag/agent export const ragPredictAnalysis = (d: object) => unwrap(api.post('/api/mes/rag/predict-analysis', d)) // 👍/👎 피드백 (solution=mes 격리) export const ragFeedback = (d: object) => unwrap(api.post('/api/mes/rag/feedback', d)) // 기법 토글 스냅샷 / 변경(MANAGER+) export const getRagToggles = () => unwrap(api.get('/api/mes/rag/toggles')) export const updateRagToggle = (key: string, value: any) => unwrap(api.put(`/api/mes/rag/toggles/${key}`, { value })) // ── Admin (SUPERADMIN / 감사로그·설정 GET 은 MANAGER+) ──────────────── export const getUsers = () => api.get('/api/admin/users') export const createUser = (data: { username: string; password: string; displayName?: string; role?: string }) => api.post('/api/admin/users', data) export const updateUserRole = (id: number, role: string) => api.put(`/api/admin/users/${id}/role`, { role }) export const updateUserActive = (id: number, active: boolean) => api.put(`/api/admin/users/${id}/active`, { active }) export const resetPassword = (id: number, password: string) => api.put(`/api/admin/users/${id}/password`, { password }) export const deleteUser = (id: number) => api.delete(`/api/admin/users/${id}`) export const getAuditLogs = (action = '', actor = '', limit = 200) => api.get(`/api/admin/audit${qs({ action, actor, limit })}`) export const getSettings = () => api.get('/api/admin/settings') export const updateSetting = (key: string, value: string) => api.put(`/api/admin/settings/${encodeURIComponent(key)}`, { value })