12. Order 서비스
client.order() 으로 접근합니다. 작업지시(Work Order)의 생성 → 시작 → 중단/재개 → 종료까지 전 생명주기를 ISA-88 기반 상태 머신으로 관리합니다.
11.1 작업지시 상태 (ISA-88 기반)
CREATE → WAIT ──start()──► START ──pause()──► PAUSE
│ │
│ resume()
│ │
▼ ▼
END ◄──end()──── START
│
└─ (또는) ──abort()──► ABORTED
| 상태 | 의미 | 전이 메서드 |
|---|---|---|
WAIT | 대기 (생성 직후 초기 상태) | create() 결과 |
START | 진행중 | start(), resume() |
PAUSE | 일시중지 | pause() |
END | 정상 완료 | end() |
ABORTED | 비정상 종료 | abort() |
상태 전이가 불가능한 호출(예: 이미
START상태에서start()재호출)은 서버가 OK +data.success=false응답을 보내며, V5 메서드는false를 반환합니다.
11.2 메서드 일람
CRUD
| 메서드 | 반환 타입 | HTTP | 엔드포인트 |
|---|---|---|---|
create(OrderRequestV5) | OrderResponseV5 또는 null | POST | /api/v5/order |
update(order_id, OrderRequestV5) | OrderResponseV5 또는 null | PUT | /api/v5/order/{id} |
delete(order_id) | boolean | DELETE | /api/v5/order/{id} |
get(order_id) | OrderResponseV5 또는 null | GET | /api/v5/order/{id} |
list() | List<OrderResponseV5> | GET | /api/v5/order |
exists(order_id) | boolean | GET | /api/v5/order/{id}/exists |
라이프사이클 (모두 boolean 반환)
| 메서드 | HTTP | 엔드포인트 |
|---|---|---|
start(order_id) | POST | /api/v5/order/{id}/start |
end(order_id) | POST | /api/v5/order/{id}/end |
pause(order_id) | POST | /api/v5/order/{id}/pause |
resume(order_id) | POST | /api/v5/order/{id}/resume |
abort(order_id) | POST | /api/v5/order/{id}/abort |
11.3 OrderRequestV5 / OrderResponseV5
| 필드 | 타입 | 설명 |
|---|---|---|
order_id | String | 작업지시 ID (PK) |
title | String | 작업 제목 |
description | String | 설명 |
site_id | String | 소속 사이트 |
asset_id | String | 대상 자산 (Equipment) |
customer_id | String | 고객 (선택) |
product_id | String | 생산할 제품 |
employee_id | String | 담당 직원 (선택) |
target_units | long | 목표 수량 |
unit | String | 단위 (EA, KG 등) |
time_per_unit_in_ms | long | 단위당 표준 시간 |
fix_start_timestamp | long | 계획 시작 시각 (밀리초) |
fix_end_timestamp | long | 계획 종료 시각 (밀리초) |
external_order_id | String | 외부 시스템 주문 ID |
status | String | (응답 전용) 현재 상태 |
11.4 사용 예시
작업지시 생성
import plantpulse.api.v5.dto.request.OrderRequestV5;
import plantpulse.api.v5.dto.response.OrderResponseV5;
OrderRequestV5 req = new OrderRequestV5();
req.setOrder_id("ORD_20260514_001");
req.setTitle("5월 14일 1교대 생산");
req.setDescription("PROD_A001 500개 생산");
req.setSite_id("SITE_DJ");
req.setAsset_id("ASSET_DJ_M_0001");
req.setProduct_id("PROD_A001");
req.setCustomer_id("CUST_001");
req.setEmployee_id("EMP_E12345");
req.setTarget_units(500);
req.setUnit("EA");
req.setTime_per_unit_in_ms(5_000);
long now = System.currentTimeMillis();
req.setFix_start_timestamp(now);
req.setFix_end_timestamp(now + 4 * 3600_000L);
req.setExternal_order_id("MES-2026-0514-001");
OrderResponseV5 created = client.order().create(req);
표준 라이프사이클
String orderId = "ORD_20260514_001";
// WAIT → START
if (!client.order().start(orderId)) {
System.err.println("시작 실패 — fix_start_timestamp가 미래이거나 이미 진행중");
return;
}
// 잠시 후 — 일시중지
client.order().pause(orderId);
// 재개
client.order().resume(orderId);
// 정상 종료
client.order().end(orderId);
비정상 종료
// abort 는 별도 인자 없이 호출. 상세 사유는 update() 로 description/notes 에 기록.
if (!client.order().abort("ORD_20260514_001")) {
System.err.println("중단 실패");
}
단건 조회 / 존재 확인
OrderResponseV5 o = client.order().get("ORD_20260514_001");
if (o != null) {
System.out.println("상태: " + o.getStatus());
System.out.println("목표: " + o.getTarget_units() + " " + o.getUnit());
}
boolean exists = client.order().exists("ORD_20260514_001");
전체 목록
import java.util.List;
List<OrderResponseV5> all = client.order().list();
for (OrderResponseV5 o : all) {
System.out.printf("[%s] %s — %s%n",
o.getStatus(), o.getOrder_id(), o.getTitle());
}
진행중 작업지시만 (클라이언트 측 필터)
import java.util.stream.Collectors;
List<OrderResponseV5> active = client.order().list().stream()
.filter(o -> "START".equals(o.getStatus())
|| "PAUSE".equals(o.getStatus()))
.collect(Collectors.toList());
자산별 작업지시
String assetId = "ASSET_DJ_M_0001";
List<OrderResponseV5> assetOrders = client.order().list().stream()
.filter(o -> assetId.equals(o.getAsset_id()))
.collect(Collectors.toList());
상태별 통계
import java.util.Map;
Map<String, Long> byStatus = client.order().list().stream()
.collect(Collectors.groupingBy(
OrderResponseV5::getStatus,
Collectors.counting()));
byStatus.forEach((status, count) ->
System.out.printf("%s: %d건%n", status, count));
수정 / 삭제
// 수정 (계획 시간 연장)
OrderRequestV5 update = new OrderRequestV5();
update.setOrder_id("ORD_20260514_001");
update.setTitle("5월 14일 1교대 생산 (연장)");
// ... 모든 필드 다시 세팅 ...
update.setFix_end_timestamp(System.currentTimeMillis() + 8 * 3600_000L);
client.order().update("ORD_20260514_001", update);
// 삭제 (취소 처리)
client.order().delete("ORD_20260514_001");
11.5 활용 시나리오
시나리오 — MES 연동 작업지시 자동 생성
import java.text.SimpleDateFormat;
import java.util.Date;
SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
String today = df.format(new Date());
// 외부 MES 큐에서 받은 주문을 V5 Order로 변환
for (MesOrder mes : mesQueue) {
OrderRequestV5 req = new OrderRequestV5();
req.setOrder_id("ORD_" + today + "_" + String.format("%03d", mes.seq));
req.setSite_id("SITE_DJ");
req.setAsset_id(mes.equipId);
req.setProduct_id(mes.productId);
req.setTarget_units(mes.qty);
req.setExternal_order_id(mes.mesOrderId);
req.setFix_start_timestamp(mes.plannedStart);
req.setFix_end_timestamp(mes.plannedEnd);
client.order().create(req);
}
시나리오 — 안전한 상태 전이 헬퍼
public static boolean safeStart(APIClient_V5 client, String orderId) {
OrderResponseV5 o = client.order().get(orderId);
if (o == null) {
log.warn("작업지시 없음: " + orderId);
return false;
}
if (!"WAIT".equals(o.getStatus())) {
log.warn("WAIT 상태가 아님: " + o.getStatus());
return false;
}
return client.order().start(orderId);
}
다음 단계
- Customer 서비스, Employee 서비스, Product 서비스 — Order에 부착되는 마스터 데이터
- Calendar 서비스 — 작업지시와 점검 일정 충돌 검사