1. 开始使用
本指南将引导您使用 V5 Java API 客户端完成首次调用。
快速开始 (60秒)
所需 3 项内容
| 项目 | 值 | 获取位置 |
|---|---|---|
| 服务器地址 | http://<server-ip>:80 (TLS 为 https://<server-ip>:443) | REST API 端点。与 Web 控制台地址相同 |
| 账号 ID | 例:api | 用于 API 调用的用户账号 |
| 令牌 | UUID 格式 | 在控制台 安全管理 → API 认证令牌 (/token/index) 中为该账号发行 → 安全设置 · 安全管理 |
只需上述三个值,即可通过以下一个代码块完成首次调用。
import java.util.List;
import plantpulse.api.v5.APIClient_V5;
import plantpulse.api.v5.dto.response.SiteResponseV5;
try (APIClient_V5 client = new APIClient_V5(
"HTTP", "100.68.69.41", 80, "api", "<issued-token>", false)) {
client.connect(); // 인증
System.out.println(client.system().ping().isPing()); // → true
for (SiteResponseV5 s : client.site().list()) // 사이트 목록
System.out.println(s.getSite_id() + " — " + s.getSite_name());
}
如果该代码块输出了 true 和站点列表,则表示准备就绪。下文 1.1~1.7 将详细说明各步骤。
1.1 添加依赖
请将 JAR 文件添加到类路径 (Classpath) 中。
# 빌드 (Gradle + Java 21)
cd plantpulse-api
./gradlew build
产出物将生成在 target/。
| 文件 | 用途 |
|---|---|
target/plantpulse-api.jar | 将其与依赖一起集成到现有项目中时使用 |
1.2 创建客户端
直接向 APIClient_V5 构造函数传递选项。
import plantpulse.api.v5.APIClient_V5;
APIClient_V5 client = new APIClient_V5(
"HTTPS", // 프로토콜: "HTTP" 또는 "HTTPS"
"100.68.69.41", // host
443, // port (REST API: 80=HTTP, 443=HTTPS)
"api", // user_id
"19ba7a54-1132-49f2-967e-3c6f5cd989a0", // token (api_key 발급용)
false); // debug
client.connect(); // 인증 + HTTP 클라이언트 초기화
// ... API 호출 ...
client.close(); // 종료
构造函数参数
| 参数 | 类型 | 说明 |
|---|---|---|
protocol | String | "HTTP" 或 "HTTPS" |
host | String | 服务器主机名或 IP |
port | int | 服务器端口 |
user_id | String | 认证账号 ID |
token | String | 已发行的令牌 (UUID 格式) |
debug | boolean | HTTP 请求/响应调试日志 |
认证流程: 调用
connect()时,内部通过user_id+token获取 api_key,随后所有调用将自动在请求头中包含该密钥。用户无需手动处理。
1.3 首次 API 调用 — ping & 站点列表
import java.util.List;
import plantpulse.api.v5.APIClient_V5;
import plantpulse.api.v5.dto.response.SiteResponseV5;
import plantpulse.api.v5.dto.response.SystemPingResponseV5;
public class FirstCall {
public static void main(String[] args) throws Exception {
try (APIClient_V5 client = new APIClient_V5(
"HTTP", "100.68.69.41", 80, "api",
"19ba7a54-1132-49f2-967e-3c6f5cd989a0", false)) {
client.connect();
// 1) 서버 헬스체크
SystemPingResponseV5 ping = client.system().ping();
System.out.println("Server alive: " + ping.isPing()
+ " (v" + ping.getVersion() + ")");
// 2) 사이트 목록 (typed DTO 리스트)
List<SiteResponseV5> sites = client.site().list();
for (SiteResponseV5 s : sites) {
System.out.println(s.getSite_id() + " — " + s.getSite_name());
}
}
}
}
执行结果:
Server alive: true (v5.0)
SITE_000001 — 대전 1공장
SITE_000002 — 천안 2공장
1.4 try-with-resources 模式
APIClient_V5 实现了 AutoCloseable。建议使用 try-with-resources 模式。
try (APIClient_V5 client = new APIClient_V5(proto, host, port, user, token, false)) {
client.connect();
// ... API 호출 ...
}
// close() 자동 호출 — HTTP 커넥션 정리
1.5 13 个服务概览
在 connect() 之后,可以通过以下服务方法访问所有域 (Lazy 初始化)。
client.system() // 헬스체크
client.site() // 사이트(공장)
client.customer() // 고객
client.employee() // 직원
client.product() // 제품
client.asset() // 자산 계층 (Area → Line → Equipment)
client.opc() // OPC 데이터 채널
client.tag() // 태그(데이터 포인트)
client.alarm() // 알람
client.order() // 작업지시
client.calendar() // 일정
client.path() // 자산 경로
client.flow() // Flow / Flow Node
1.6 V5 方法签名标准模式
所有域服务均遵循一致的方法模式。仅域名称不同,使用方式相同。
| 模式 | 返回类型 | 失败时的返回值 | 说明 |
|---|---|---|---|
create(req) | *ResponseV5 | null | 注册 |
update(id, req) | *ResponseV5 | null | 全量修改 |
delete(id) | boolean | false | 删除 |
get(id) | *ResponseV5 | null | 单项查询 |
list() | List<*ResponseV5> | 空列表 | 全量列表 |
exists(id) | boolean | false | 存在性确认 |
count() | long | 0 | 计数 |
⚠️ 不会抛出异常。 通信错误或服务器 ERROR 响应将返回上述表格中的安全默认值。请通过日志或
BaseServiceV5.getErrorCode(env)确认错误原因。详细内容请参考 响应格式与错误处理。
1.7 调试模式
将构造函数的最后一个参数设置为 true,即可在控制台输出 HTTP 请求和响应。
APIClient_V5 client = new APIClient_V5(
"HTTP", "100.68.69.41", 80, "api", TOKEN, true); // debug=true
输出示例:
[V5] GET https://server/api/v5/site headers={api_key=ab****cd}
[V5] response: 200 {"_status":"OK","data":[{"site_id":"SITE_000001",...}]}
1.8 认证失败时 — 503 并非「密钥错误」
connect() 使用 POST /api/v5/auth 将令牌与 api_key 交换。成功判定并非 HTTP 200 — 必须满足 meta.status 为 "OK" 且 data.api_key 不为空 才是成功。之所以有第二个条件,是因为确实存在令牌校验通过但密钥发行失败的路径(此时服务器返回 500)。
curl -s -X POST "http://<server>/api/v5/auth" \
-H "Content-Type: application/json; charset=UTF-8" \
-d '{"token":"<issued-token>"}'
# 200: {"data":{"api_key":"..."},"meta":{"status":"OK"},"errors":null}
| 响应 | 代码 | 含义 | 修正方向 |
|---|---|---|---|
| 400 | E1001 | token 缺失或为空 | 请求正文 |
| 422 | E1002 | 正文非 JSON 格式 | 请求正文 |
| 401 | E1100 | 令牌错误 | 凭据 |
| 401 | E1102 | 令牌过期 | 重新发行即可恢复 |
| 401 | E1103 | 令牌已撤销 | 原令牌无法恢复 — 请获取新令牌 |
| 429 | E1101 | 同一 IP 失败次数过多 | 等待 Retry-After 后,修正值后再试 |
| 500 | E5000 | 令牌正确但 api_key 发行失败 | 服务器端 — 非凭据问题 |
| 503 | E5500 | 无法触达令牌存储,«无判定结果» | 服务器端 — 非凭据问题 |
当服务器无法触达令牌存储 (PostgreSQL) 时,不会返回 401。401 是一种「您的凭据错误」的主张,而此时服务器并不知道令牌是否正确。如果调用者无法区分两者,会导致严重的排查偏差。
实际上曾发生过此类情况 — 2026-09-07,夜间 e2e 连续八天报告「22/22 失败, E1100 Token authentication failed」,但平台日志中的真实原因为 Connection to plantpulse-datalake:5432 refused。PostgreSQL 故障在八天时间内伪装成了凭据错误。 目前,系统会追踪至存储异常 (SQLException · Socket 系列) 并通过 503 E5500 响应。
基于同样原因,503 不计入失败次数。 否则 DB 故障会将正常的调用者锁定在 429 状态 — 即在故障之上叠加锁定。
Java 客户端会将此区分通过异常消息直接返回。请根据消息决定修正位置。
| 消息 | 含义 |
|---|---|
V5 connect: /api/v5/auth call failed — … | 请求未能触达服务器 (地址/端口/防火墙) |
V5 connect: auth endpoint unavailable (http=503) — server reached no verdict about the token | 服务器无法做出判定 — 请勿怀疑令牌 |
V5 connect: authentication failed — … | 401/400 系列 — 修正凭据或请求 |
V5 connect: api_key issuance failed — response=… | meta.status 正确但密钥为空 — 服务器端问题 |
同一 IP 的认证失败累积至 engine.api.v5.auth.bruteforce.ip_limit (默认 30) 次时,将在 engine.api.v5.auth.bruteforce.window.minutes (默认 15) 分钟内被 429 锁定。
可通过 engine.api.v5.auth.bruteforce.enabled=false 关闭此功能,但关闭后将无法抵御令牌暴力破解攻击。非 JSON 格式的请求不被视为凭据尝试,因此不计入次数。