API 文档
IoT DC3 的 REST 接口文档由代码注解自动生成,经网关聚合成一个统一的 Swagger UI。读完这页,你能在开发环境打开各中心的在线文档、用默认凭据走完"取盐 → 取 token → 带鉴权头调用"的登录流、看懂 CRUD 路径约定,并理解每个接口上的 x-dc3-ai 风险元数据是怎么喂给 AI/MCP 工具的。
你在这里:准备调用或调试后端接口。要先把环境跑起来,看 第一个设备;要理解鉴权头背后的租户与权限,看 鉴权·租户·RBAC。
文档从哪来:注解生成,网关聚合
平台不维护任何手写的 API 规格文件。每个接口的标题、参数、请求/响应模型,全部来自 Controller 上的 springdoc-openapi 注解(@Tag、@Operation、@Parameter、@Schema),运行时由各中心服务在 WebFlux 栈上生成自己的 OpenAPI JSON。
四个业务中心——鉴权中心(Auth Center / dc3-center-auth)、管理中心(Manager Center / dc3-center-manager)、数据中心(Data Center / dc3-center-data)、智能中心(Agentic Center / dc3-center-agentic)——各自暴露一份文档。网关(Gateway / dc3-gateway)本身没有业务 Controller,它通过 springdoc.swagger-ui.urls 把这四份文档聚合到一个带服务下拉选择器的 Swagger UI 里,对外只暴露一个入口。
分组靠两层配置完成:dc3-common-web 的 SpringDocConfig 提供全局元信息(标题、版本、联系人、许可证、安全方案),各业务模块在自己已扫描的包下声明 GroupedOpenApi Bean,只扫描本模块 Controller。各中心服务的 spring.webflux.base-path(如 /auth、/manager)会加到文档路径前,例如 /auth/v3/api-docs;网关聚合路径 /v3/api-docs/{svc} 抹平了这层差异,统一访问。
dc3-center-single 模式
dc3-center-single 把多个业务模块打进一个进程,所以它的 Swagger UI 里会同时出现多个分组——这是预期行为,不是配置重复。
访问入口
开发环境下,优先用网关聚合入口;调试单个中心时也可以直连它的 base-path 文档。
| 目标 | URL |
|---|---|
| 网关聚合 UI(推荐) | http://<gateway>:8000/swagger-ui.html |
| 鉴权中心直连 | http://<auth>:8300/auth/swagger-ui.html |
| 管理中心直连 | http://<manager>:8400/manager/swagger-ui.html |
| 数据中心直连 | http://<data>:8500/data/swagger-ui.html |
| 智能中心直连 | http://<agentic>:8600/agentic/swagger-ui.html |
| 单中心 OpenAPI JSON | http://<center>:<port>/<svc>/v3/api-docs |
登录与鉴权:取盐 → 取 token → 带 X-Auth-* 头
除 /api/v3/auth/token/**(取盐、生成 token、改密)这类公开端点外,网关后的所有业务接口都要求携带三个鉴权头:X-Auth-Tenant、X-Auth-Login、X-Auth-Token。登录本身是一个两步握手:先用用户名+租户向服务端要一个临时盐(5 分钟有效),再把 md5(password) 连同这枚盐一起提交换取 token(12 小时有效)。盐不参与密码哈希——它作为独立字段单独传给服务端,服务端用它给 token 签名;密码本身只做单次 md5,由后端 PasswordUtil.verify 校验。
实际调用形如下面这样(示例值仅作演示,default/dc3 是种子数据自带的默认租户与用户):
# 1. 取盐
curl -X POST http://localhost:8000/api/v3/auth/token/salt \
-H 'Content-Type: application/json' \
-d '{"tenant":"default","name":"dc3"}'
# → R<String>:data 即 salt(示例:"f3a9c1..."),5 分钟有效
# 2. 本地计算 md5(password),连同 salt 一起换 token
curl -X POST http://localhost:8000/api/v3/auth/token/generate \
-H 'Content-Type: application/json' \
-d '{"tenant":"default","name":"dc3","salt":"f3a9c1...","password":"<md5(password)>"}'
# → R<String>:data 即 access token(示例:"eyJ..."),12 小时有效
# 3. 带鉴权头调用业务接口
curl -X POST http://localhost:8000/api/v3/manager/device/list \
-H 'X-Auth-Tenant: default' \
-H 'X-Auth-Login: dc3' \
-H 'X-Auth-Token: {"salt":"f3a9c1...","token":"eyJ..."}' \
-H 'Content-Type: application/json' \
-d '{"current":1,"size":10}'# CLI 封装了取盐→哈希→换 token→保存凭据的全过程
dc3 config set gateway http://localhost:8000
dc3 auth login --tenant default --username dc3在 Swagger UI 里调试受保护接口时,点右上角 Authorize,按下表填入鉴权头即可:
| Header | 示例值 |
|---|---|
X-Auth-Tenant | default |
X-Auth-Login | dc3 |
X-Auth-Token | {"salt":"...","token":"..."} |
不要把真实凭据写进文档/日志/issue
token、password、salt、api key 一律不入库到文档、提交记录或工单。示例里的哈希值、token 都用占位符代替。
CRUD 路径约定:动词反映结果基数
所有业务接口的命名遵循同一条规则——HTTP 路径、Java 方法、gRPC RPC、前端函数上的动词,必须反映返回结果的基数。读单条用 getXxx,读集合用 listXxx,写入三件套是 add/update/delete。这让你看到路径就能判断它返回一条还是一批、是读还是写。
| 动作 | Java 方法 | HTTP 路径 | gRPC RPC | 前端函数 |
|---|---|---|---|---|
| 单条记录 | getXxx(...) | /get_xxx | GetXxx | getXxx(...) |
| 集合 | listXxx(...) | /list_xxx | ListXxx | listXxx(...) |
| 新增 | add(BO) | /add | n/a | addXxx(...) |
| 更新 | update(BO) | /update | n/a | updateXxx(...) |
| 删除 | delete(Long) | /delete | n/a | deleteXxx(...) |
保留动词的专用语义
select* 只用于 *ManagerImpl 里的原生 MyBatis Mapper 调用;remove* 只用于 MyBatis-Plus 继承来的 Manager 方法。业务删除一律用 delete*。find*/query*/fetch* 不作为主 CRUD 动词。
举一个走管理中心的真实例子(黄金路径中"新增设备"一步):接口是 POST /api/v3/manager/device/add,请求体是 DeviceVO,关键字段 deviceName、driverId、profileId、enableFlag,成功返回成功码 SuccessCode.ADD("Added successfully")——add 不回传新建实体 ID,后续若需要这个 id,得用 device/list 按名称查回。需要 device:add 权限。接口统一用 R<T> 响应封装,含 ok、code、message、data 四字段。
x-dc3-ai:给 AI/MCP 工具的风险标注
每个接口的 @Operation 上可以挂一段 x-dc3-ai OpenAPI 扩展,用四个布尔/枚举属性描述这次调用对 AI Agent 意味着什么风险。这段元数据不是写给人看的注释——它会被 MCP 工具目录聚合器读取,落进 dc3_mcp_tool_catalog 表,进而决定一个工具在 tools/list 里是否对某个 AI 连接可见、调用时是否需要二次确认。
@Extension(name = "x-dc3-ai", properties = {
@ExtensionProperty(name = "riskLevel", value = "MEDIUM"), // LOW / MEDIUM / HIGH
@ExtensionProperty(name = "destructive", value = "false"), // 是否破坏数据/配置
@ExtensionProperty(name = "idempotent", value = "false"), // 是否可安全重试
@ExtensionProperty(name = "openWorld", value = "true") // 是否触达外部/物理世界
})各属性的含义:
riskLevel:LOW/MEDIUM/HIGH,按动词语义约定人工标注(不是由代码从 HTTP 方法自动推导)——惯例上delete标HIGH、add/update标MEDIUM、get/list标LOW,但最终取值来自每个@Operation上手写的注解,聚合器只在缺失或非法时兜底为HIGH。HIGH风险工具默认对 AI 隐藏,需显式启用,且调用走两阶段确认。destructive:调用是否会破坏既有数据或设置(如改密、取消 token)。idempotent:同样参数重复调用是否安全(决定失败后能否自动重试)。openWorld:是否会触达平台之外的外部系统或物理设备(如下发写命令)。
以鉴权中心 TokenController 为例,取盐接口标注 riskLevel=LOW, destructive=false, idempotent=false, openWorld=false,而生成 token 接口标注 riskLevel=HIGH——两者都属公开端点、对 AI 工具目录隐藏(hidden=true),但风险等级如实区分。智能中心的 POST /api/v3/agentic/v1/chat/completions 则标注 riskLevel=MEDIUM, destructive=false, idempotent=false, openWorld=true。
聚合器还会从 HTTP 方法补出 read_only_hint(GET → 1,POST → 0),把全部提示位(destructive_hint/idempotent_hint/open_world_hint/read_only_hint,取值 0/1)连同 risk_level 一起持久化。AI Agent 通过 MCP 看到的工具风险,就是这套标注的最终呈现。完整的 MCP 工具暴露、过滤与确认机制见 AI Agent / MCP 集成。
导出 OpenAPI JSON
需要离线契约快照、或喂给客户端代码生成器时,从运行中的开发/测试栈一键导出各中心的 OpenAPI JSON:
make openapi可通过变量覆盖导出入口与输出目录:
make openapi OPENAPI_BASE=http://localhost:8000 OPENAPI_OUT=build/openapi新增接口时的文档要求
给后端加接口时,文档不是事后补的——注解就是文档源:
- Controller 类加
@Tag(name = "...", description = "...")。 - 方法加
@Operation(summary = "...", description = "..."),摘要遵循 CRUD 动词约定(add/delete/update/getXxx/listXxx)。 - 路径、查询、请求体参数加
@Parameter。 - 请求/响应 DTO 字段加
@Schema(description = ...),必要时补example与requiredMode = REQUIRED。 - 涉及 AI/MCP 可调用的接口,按真实风险补
x-dc3-ai扩展。 - 新增业务模块时,补
GroupedOpenApiBean、网关聚合配置和 Swagger UI 分组。
注解文字一律用英文
注解里的 summary/description 属于用户可见代码文本,按工程规则用英文书写;同时不要在 @Schema 的 example 里放 apiKey、password、secret、token 等敏感值。
生产环境关闭 Swagger / OpenAPI 暴露
API 文档仅在 dev、test、pre 环境可用,生产环境(pro profile)由每个中心服务各自的 application-pro.yml(auth/manager/data/agentic/single 各一份)关闭:
springdoc:
api-docs:
enabled: false
swagger-ui:
enabled: false共享的 application-web.yml 只设 springdoc 的基线路径,不负责禁用——它的注释也写明禁用动作落在各服务的 application-pro.yml。生产环境中 springdoc 端点根本不存在,因此不会暴露任何文档内容。
延伸阅读
- 鉴权·租户·RBAC — 鉴权头背后的取盐/token/HMAC 与租户隔离、权限模型
- 第一个设备 — 用
dc3CLI 走完黄金路径,把这些接口实际跑一遍 - AI Agent / MCP 集成 —
x-dc3-ai元数据如何变成 MCP 工具风险策略与两阶段确认 - 测试 — 接口契约与集成测试如何验证这些路径