> For the complete documentation index, see [llms.txt](https://kopens.gitbook.io/plantpulse-platform/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://kopens.gitbook.io/plantpulse-platform/developer/api-manual/asset-service.md).

# 7. Asset 서비스

`client.asset()` 으로 접근합니다. PlantPulse 자산 모델의 핵심 — **Site > Area > Line > Equipment** 의 계층 트리를 관리합니다.

> **ID 규칙**: `ASSET_` 접두사 필수. 타입 코드 `A`/`L`/`M`이 트리 계층을 결정합니다. 자세한 내용은 [도메인 ID 규칙](https://kopens.gitbook.io/plantpulse-platform/developer/api-manual/pages/iFWQog8NrVK7oN943IC1#24-asset-id--계층-구조의-핵심) 참고.

## 7.1 메서드 일람

| 메서드                                | 반환 타입                       | HTTP   | 엔드포인트                       |
| ---------------------------------- | --------------------------- | ------ | --------------------------- |
| `create(AssetRequestV5)`           | `AssetResponseV5` 또는 `null` | POST   | `/api/v5/asset`             |
| `update(asset_id, AssetRequestV5)` | `AssetResponseV5` 또는 `null` | PUT    | `/api/v5/asset/{id}`        |
| `delete(asset_id)`                 | `boolean`                   | DELETE | `/api/v5/asset/{id}`        |
| `get(asset_id)`                    | `AssetResponseV5` 또는 `null` | GET    | `/api/v5/asset/{id}`        |
| `list()`                           | `List<AssetResponseV5>`     | GET    | `/api/v5/asset`             |
| `exists(asset_id)`                 | `boolean`                   | GET    | `/api/v5/asset/{id}/exists` |

> Asset 서비스는 단순화된 CRUD에 집중되어 있습니다. 트리 계층 조회는 [Path 서비스](/plantpulse-platform/developer/api-manual/path-service.md) 또는 클라이언트 측 필터링(`list()` 후 `asset_type` 필터)을 사용하시면 됩니다.

## 7.2 Asset Type 값

| asset\_type | 의미                      |
| ----------- | ----------------------- |
| `A`         | Area (구역)               |
| `L`         | Line (라인)               |
| `M`         | Equipment (설비, Machine) |

## 7.3 AssetRequestV5 / AssetResponseV5

| 필드                            | 타입     | 설명                       |
| ----------------------------- | ------ | ------------------------ |
| `asset_id`                    | String | 자산 ID (PK, `ASSET_` 필수)  |
| `asset_name`                  | String | 표시명                      |
| `parent_asset_id`             | String | 부모 자산 ID                 |
| `asset_order`                 | String | 형제 간 정렬 순서               |
| `asset_type`                  | String | `A`/`L`/`M`              |
| `table_type`                  | String | 설비 분류 (CNC, INJECTION 등) |
| `site_id`                     | String | 소속 사이트                   |
| `description`                 | String | 설명                       |
| `insert_date` / `update_date` | String | (응답 전용) 시각               |

## 7.4 사용 예시

### Area → Line → Equipment 트리 생성

```java
import plantpulse.api.v5.dto.request.AssetRequestV5;

// 1) Area
AssetRequestV5 area = new AssetRequestV5();
area.setSite_id("SITE_DJ");
area.setParent_asset_id("SITE_DJ");
area.setAsset_id("ASSET_DJ_A_0001");
area.setAsset_name("조립구역");
area.setAsset_type("A");
client.asset().create(area);

// 2) Line
AssetRequestV5 line = new AssetRequestV5();
line.setSite_id("SITE_DJ");
line.setParent_asset_id("ASSET_DJ_A_0001");
line.setAsset_id("ASSET_DJ_L_0001");
line.setAsset_name("1번 조립 라인");
line.setAsset_type("L");
client.asset().create(line);

// 3) Equipment (CNC 머신)
AssetRequestV5 cnc = new AssetRequestV5();
cnc.setSite_id("SITE_DJ");
cnc.setParent_asset_id("ASSET_DJ_L_0001");
cnc.setAsset_id("ASSET_DJ_M_0001");
cnc.setAsset_name("CNC #1");
cnc.setAsset_type("M");
cnc.setTable_type("CNC");
cnc.setDescription("DMG MORI NLX-2500");
client.asset().create(cnc);
```

### 단건 조회

```java
import plantpulse.api.v5.dto.response.AssetResponseV5;

AssetResponseV5 cnc = client.asset().get("ASSET_DJ_M_0001");
if (cnc != null) {
    System.out.println(cnc.getAsset_name() + " (" + cnc.getAsset_type() + ")");
    System.out.println("부모: " + cnc.getParent_asset_id());
}
```

### 전체 목록 + 타입별 필터링

V5는 `listByType` 메서드가 없으므로 클라이언트 측에서 필터링합니다.

```java
import java.util.List;
import java.util.stream.Collectors;

List<AssetResponseV5> all = client.asset().list();

// Equipment만
List<AssetResponseV5> equipments = all.stream()
        .filter(a -> "M".equals(a.getAsset_type()))
        .collect(Collectors.toList());

// 대전공장의 Line만
List<AssetResponseV5> djLines = all.stream()
        .filter(a -> "SITE_DJ".equals(a.getSite_id()))
        .filter(a -> "L".equals(a.getAsset_type()))
        .collect(Collectors.toList());
```

### 트리 재구성 (클라이언트 측)

```java
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;

// asset_id 인덱스
Map<String, AssetResponseV5> byId = new HashMap<>();
for (AssetResponseV5 a : client.asset().list()) {
    byId.put(a.getAsset_id(), a);
}

// 부모-자식 매핑
Map<String, List<AssetResponseV5>> children = new HashMap<>();
for (AssetResponseV5 a : byId.values()) {
    children
        .computeIfAbsent(a.getParent_asset_id(), k -> new ArrayList<>())
        .add(a);
}

// 사이트(SITE_DJ) 산하 트리 출력
printTree(children, "SITE_DJ", 0);

static void printTree(Map<String, List<AssetResponseV5>> children, String id, int depth) {
    List<AssetResponseV5> kids = children.getOrDefault(id, List.of());
    for (AssetResponseV5 c : kids) {
        System.out.println("  ".repeat(depth) + c.getAsset_id() + " — " + c.getAsset_name());
        printTree(children, c.getAsset_id(), depth + 1);
    }
}
```

### 자산 수정

```java
AssetRequestV5 update = new AssetRequestV5();
update.setSite_id("SITE_DJ");
update.setParent_asset_id("ASSET_DJ_L_0001");
update.setAsset_id("ASSET_DJ_M_0001");
update.setAsset_name("CNC #1 (보수 후)");
update.setAsset_type("M");
update.setTable_type("CNC");
update.setDescription("2026-05-10 베어링 교체 완료");

client.asset().update("ASSET_DJ_M_0001", update);
```

### 자산 삭제

> ⚠️ 자산 삭제 전에 그 자산에 연결된 모든 Tag·하위 Asset을 먼저 삭제해야 합니다.

```java
boolean ok = client.asset().delete("ASSET_DJ_M_OLD");
```

## 7.5 활용 시나리오

### 시나리오 — 사이트의 Equipment 목록과 각 Tag 수

```java
String siteId = "SITE_DJ";

List<AssetResponseV5> equipments = client.asset().list().stream()
        .filter(a -> siteId.equals(a.getSite_id()))
        .filter(a -> "M".equals(a.getAsset_type()))
        .collect(Collectors.toList());

for (AssetResponseV5 eq : equipments) {
    long tagCount = client.tag().countByAsset(eq.getAsset_id());
    System.out.printf("%s — Tag %d개%n", eq.getAsset_name(), tagCount);
}
```

### 시나리오 — Line 단위 OEE 대시보드 데이터 수집

```java
// 1) Line 목록
List<AssetResponseV5> lines = client.asset().list().stream()
        .filter(a -> "L".equals(a.getAsset_type()))
        .filter(a -> "SITE_DJ".equals(a.getSite_id()))
        .collect(Collectors.toList());

// 2) 각 Line 산하 Equipment 조회 → 자산 경로 → 작업지시 → KPI 집계
for (AssetResponseV5 line : lines) {
    List<AssetResponseV5> machines = client.asset().list().stream()
            .filter(a -> line.getAsset_id().equals(a.getParent_asset_id()))
            .collect(Collectors.toList());

    for (AssetResponseV5 m : machines) {
        long now = System.currentTimeMillis();
        // 현재 작업지시는 Order 서비스로 조회
        // ...
    }
}
```

## 다음 단계

* [Tag 서비스](/plantpulse-platform/developer/api-manual/tag-service.md) — 자산에 태그 연결
* [Path 서비스](/plantpulse-platform/developer/api-manual/path-service.md) — 트리 경로 한 번에 조회
