三端(嵌入式网关 / Windows GUI / 云端)唯一的 REST 契约。
MQTT 契约见 mqtt-device-protocol.md;联调矩阵见 integration-test-plan.md。
制定:主代理(2026-10-01)。任何路由/字段变更只能由主代理发起,三端同步改。
版本注记:v1.1(2026-10-02,三端联调修复批)。变更记录: - 登录端点定稿
POST /api/v1/auth/login及返回字段(第一节写死);新增 refresh/logout; - §2 设备/latest/history 响应形状按云端 P1 实现现状改写(此前三方各表述),三个读端点改为必须带 JWT + 租户范围校验; - 新增第四节「GUI 版本管理」端点(版本列表/发布/包上传/设最新/下架/force 标记); - 角色与范围语义写进第五节(客户用户/运营/管理员/超级管理员,平台侧干预必须落审计)。
AppSettings.ApiBaseUrl,只填 scheme+host+port(如 http://127.0.0.1:8080)/api/v1 前缀(见 saas-auth.md);数据类端点用云端 P1 已实现并联调通过的形状(下表)。saas-auth.md 对齐)| 方法 | 端点 | 说明 |
|---|---|---|
POST |
/api/v1/auth/login |
{username,password} → 登录响应(见下) |
POST |
/api/v1/auth/refresh |
{refresh_token} → 同登录响应形状 |
POST |
/api/v1/auth/logout |
{refresh_token} → 吊销该 refresh token |
登录响应字段(写死,不许增删改名):
{
"access_token": "<JWT>",
"refresh_token": "<JWT>",
"expires_in_s": 86400,
"tenant_id": "t-xxxx",
"mqtt": {
"host": "broker.example.com",
"port": 1883,
"username": "{tenant_id}/{username}",
"password": "<短期 MQTT 凭证>",
"expires_in_s": 86400
}
}
access_token / refresh_token:JWT,靠 typ claim 区分(access / refresh);access 默认 24h。mqtt.username = {tenant_id}/{username}(GUI principal);mqtt.password = access_token 本身(Broker 回调按 JWT 校验);mqtt.host/mqtt.port 为 GUI 实际连接的 Broker 公共端点。Authorization: Bearer <access_token>;401 → GUI 弹回登录页。POST /api/v1/login(只回 {token})保留兼容,标记 deprecated,新客户端一律用 /api/v1/auth/login。三个读端点(设备列表 / latest / history)必须带 JWT 并做租户范围校验:客户用户只限本租户,平台角色只限所辖租户(见第五节),跨界 403、无凭据 401。
| 方法 | 端点 | 说明 |
|---|---|---|
GET |
/healthz |
健康检查 {"ok":true,"status":"ok","telemetry_parse_failures":N}(N 为遥测解析失败/空点累计丢弃计数) |
GET |
/api/devices?tenant={tid} |
设备列表:[{"tenant","device","last_seen","version","online"}],last_seen 为毫秒时间戳或 null |
GET |
/api/devices/{tenant}/{device}/latest |
最新值数组:[{"ts","area","address","data_type","value","quality","source","unit"}],ts 为毫秒时间戳 |
GET |
/api/telemetry?tenant=&device=&area=&address=&from=&to=&limit= |
历史点数组,元素形状同 latest;from/to 为毫秒时间戳 |
POST |
/api/devices/{tenant}/{device}/cmd |
下行命令 {cmd_id,action,params} → 等待 cmd_ack 配对;设备无回执时 504 |
tenant 一律显式传参或进路径,不信任载荷自带值(多租户隔离,见安全审计)。Services/Api/ApiClient.cs 的数据类方法必须按 §2 的端点实现(tenant 从 SessionInfo.TenantId 取)。/ 根路径是 P1 的单文件看板(调试用),web/ 是正式前端(P2 对齐登录后替换)。云端维护桌面 GUI 的版本与安装包,GUI 启动后带 JWT 查新。包文件先存云端本地目录(APP_PACKAGE_DIR 配置,后续可换对象存储),服务端算 SHA256。
版本对象字段:version(语义版本字符串)、notes(更新说明)、package_url(下载相对路径)、sha256(小写 hex)、size(包字节数)、min_supported_version(低于此版本必须强制更新,可为 null)、force_update(是否强制更新)、is_latest(是否当前最新)、status(published 上架 / unpublished 下架)、published_at(ISO 8601 时间)。
| 方法 | 端点 | 说明 |
|---|---|---|
GET |
/api/v1/app/latest |
(带 JWT,任意角色)当前最新已上架版本对象;没有时 404 |
GET |
/api/v1/app/versions |
版本列表(平台角色:运营/管理员/超级管理员) |
POST |
/api/v1/app/versions?version=¬es=&min_supported_version=&force_update=&filename= |
发布:body 为包原始字节(管理员/超级管理员);服务端存盘、算 SHA256/size 后落库,201 返回版本对象 |
GET |
/api/v1/app/packages/{filename} |
下载包字节(带 JWT,package_url 即此路径) |
POST |
/api/v1/app/versions/{version}/latest |
设为最新(管理员/超级管理员;同刻只有一个 latest) |
POST |
/api/v1/app/versions/{version}/offline |
下架(管理员/超级管理员);下架版本不再被 /api/v1/app/latest 返回 |
POST |
/api/v1/app/versions/{version}/force |
改强制更新标记 {force_update: bool}(管理员/超级管理员) |
role claim:customer(客户用户)/ operator(平台运营)/ admin(平台管理员)/ super_admin(超级管理员)。