按周工交付的 SEC-Z-01 清理后源码建立 M-Z 主干,并按 Git 仓库惯例重排目录。 - aomin-gateway/ 后端工程 87 文件,原样保留,路径与构建命令未变 - demo/ 演示前端与设备模拟器 9 文件(原 02_前端与设备端),非正式三端 - docs/handover/ 交付与安全清理材料 6 份,内容未改 - docs/contract/ J-00 规格、OpenAPI、内部五接口、状态机、Mock 样例 - docs/reference/ 指纹与校验清单,含 84 个源码文件的 sha256 清单 目录重排只动位置不动内容:源码树 84 文件哈希与交付一致,逐文件自校验通过; 12 条 Flyway 迁移与交付包内镜像逐字节一致。 未纳入:迁移镜像目录(与工程内一套重复,改留指纹)、target/、证据 raw、 应急恢复脚本、评审用虚构台账数据。 安全:SEC-Z-01 15 处明文已清零,导入前重新扫描无新增命中,本仓库无历史污染。 行尾按交付字节原样保存(.gitattributes 设 * -text),不做归一化以免整树摘要失效。
139 lines
6.9 KiB
Markdown
139 lines
6.9 KiB
Markdown
# J-00 OpenAPI 机器可读版 · 使用说明
|
||
|
||
**文件**:`6_J-00_接入接口规格_V1.8_OpenAPI.yaml`(OpenAPI 3.0.3,v1.8.0)—— **与本说明放在同一目录**,可直接用编辑器或 Swagger Editor 打开
|
||
**对应**:`6_J-00_接入接口规格_V1.8.md`(同一个契约的人读版)
|
||
**编制**:周工 | 2026-11-02
|
||
|
||
> **为什么文件名这么长**:这是故意的。同一份契约有两个形态——`.md` 讲"为什么",`.yaml` 讲"长什么样"。两个文件共用 `6_J-00_接入接口规格_V1.8` 前缀,在文件夹里**永远相邻**,谁也漏不掉;仅仅一个叫 `internal-iot.yaml` 的文件,脱离上下文就没人知道它是 J-00 的机读版。
|
||
|
||
---
|
||
|
||
## 一、这是什么
|
||
|
||
一份**机器可读的接口契约**。内容和 J-00 V1.8 一一对应,只是换了一种能被工具直接消费的写法。
|
||
|
||
**两份文件的分工,不要理解成两份契约:**
|
||
|
||
| | 管什么 | 举例 |
|
||
|---|---|---|
|
||
| **本文件(YAML)** | **长什么样** | 路径、方法、字段类型、必填、枚举、长度上限、错误结构 |
|
||
| **J-00 V1.8** | **为什么这样** | 告警状态机、四条硬边界、幂等三档算法、断档与 0x04、`tsSource` 为什么不能伪造 |
|
||
|
||
有些东西**写不进 YAML**,比如「`deviceTime` 没有就传 null,禁止用 `receivedAt` 顶替——填了等于伪造时源」。所以:**字段定义以本文件为准,业务语义的解释权仍在 J-00。**
|
||
|
||
两者不一致时,以本文件为准,同时回改 J-00,两边同版本号。
|
||
|
||
---
|
||
|
||
## 二、里面有什么
|
||
|
||
| 项 | 数量 | 说明 |
|
||
|---|---:|---|
|
||
| 接口 | **6** | 在役 5 个(I-01/02/05/12/18)+ 二期预留 1 个(I-17) |
|
||
| schema | 20 | Event/DeviceStatus/RawFrame/Command/CommandResult 及其批次包装 |
|
||
| 公共参数 | 8 | 鉴权头 4 个 + 幂等键 4 个(每接口一个) |
|
||
| 公共响应 | 4 | 400/401/429/500 |
|
||
|
||
**五个在役接口:**
|
||
|
||
| 编号 | 方法 | 路径 | `operationId` |
|
||
|---|---|---|---|
|
||
| `I-01` | POST | `/events/batch` | `ingestEvents` |
|
||
| `I-02` | POST | `/device-status/batch` | `ingestDeviceStatus` |
|
||
| `I-12` | POST | `/raw-frames` | `ingestRawFrames` |
|
||
| `I-18` | GET | `/commands/pending` | `pullPendingCommands` |
|
||
| `I-05` | POST | `/commands/result` | `reportCommandResults` |
|
||
| `I-17` | POST | `/commands/{commandId}/dump` | 二期预留,**本期固定 404,不要实现** |
|
||
|
||
**枚举已全部写死,且与 J-00 逐值一致:**
|
||
|
||
| 枚举 | 值数 | 内容 |
|
||
|---|---:|---|
|
||
| `eventType` | **29** | 10 对发生/解除 + 9 个单值 |
|
||
| `sourceChannel` | 4 | `OWN_GW`/`HORN_HOST`/`HORN_CELL`/`MIR_CELL` |
|
||
| `result` | 5 | `ACKED`/`SENT`/`TIMEOUT`/`DEVICE_OFFLINE`/`FAILED` |
|
||
| `rejectReason` | 4 | `RATE_LIMITED`/`SCHEMA_INVALID`/`UNKNOWN_CHANNEL`/`SENSOR_ID_INVALID` |
|
||
| `cmdType` | 8 | `set_mode`/`set_ack_policy`/`time_sync`/`set_report_cfg`/`del_sensor`/`reboot`/`diag_dump`/`set_ota` |
|
||
|
||
---
|
||
|
||
## 三、能拿它做什么(五件事)
|
||
|
||
> 下表里的 `$Y` 指本文件名。因为它带中文和版本号,直接敲命令很累,下面给了变量写法,复制即用。
|
||
|
||
| 用途 | 怎么用 | 省了什么 |
|
||
|---|---|---|
|
||
| **接口校验** | 用 `swagger-cli validate` 或编辑器插件跑一遍 | 字段漏写、枚举越界当场暴露,不用等联调 |
|
||
| **生成服务端骨架** | `openapi-generator-cli generate -i "$Y" -g spring` | 五个 Controller 的入参出参自动生成,外包只填业务 |
|
||
| **生成前端类型** | `-g typescript-fetch` 或 `typescript-axios` | 陈工不用照表格手敲 TS 类型 |
|
||
| **起 Mock 服务** | `prism mock "$Y"` | 前端不用等后端就能联调 |
|
||
| **生成测试用例** | `-g jmeter` 或 `schemathesis run` | 必填、枚举、边界自动出用例,比手写清单全 |
|
||
|
||
**推荐的命令(外包侧):**
|
||
|
||
```bash
|
||
# 文件名太长,先存成变量,后面照抄即可
|
||
Y="6_J-00_接入接口规格_V1.8_OpenAPI.yaml"
|
||
|
||
# 1. 语法与规范校验
|
||
npx @redocly/cli lint "$Y"
|
||
|
||
# 2. 生成 Spring Boot 服务端接口骨架
|
||
npx @openapitools/openapi-generator-cli generate \
|
||
-i "$Y" -g spring \
|
||
-o ./generated --additional-properties=interfaceOnly=true
|
||
|
||
# 3. 起 Mock 服务(前端联调用)
|
||
npx @stoplight/prism-cli mock "$Y" -p 4010
|
||
```
|
||
|
||
---
|
||
|
||
## 四、与 J-00 V1.8 的逐项核对
|
||
|
||
已用脚本比对,以下全部一致:
|
||
|
||
| 项 | J-00 位置 | YAML 位置 |
|
||
|---|---|---|
|
||
| 五个接口的编号与路径 | §1.1 | `paths` |
|
||
| `eventType` 29 值 | §1.5.1 | `schemas.EventType.enum` |
|
||
| 事件对象 12 个字段与必填性 | §1.3 | `schemas.Event` |
|
||
| I-02 十个字段 | §1.8 | `schemas.DeviceStatus` |
|
||
| I-12 八个字段(含 `rawFrame` 定死 Base64) | §1.9.2 | `schemas.RawFrame` |
|
||
| `result` 5 值 | §3.3 | `schemas.CommandResult.result` |
|
||
| I-18 四个查询参数与上限 | §3.2 | `paths./commands/pending.parameters` |
|
||
| 鉴权四层 + 幂等键算法 | §1.2 | `components.parameters`(每个接口的幂等键**分别写**,因为取法不同) |
|
||
| 响应结构与 `unregistered` 非错误语义 | §1.7 | `schemas.IngestResponse` |
|
||
|
||
**幂等键按接口分开写,这是 V1.6 的重点修正:**
|
||
|
||
| 接口 | 幂等键 | 为什么不能统一 |
|
||
|---|---|---|
|
||
| I-01 | `{sourceChannel}-{首条 eventId}-{条数}` | 有 `eventId` |
|
||
| **I-02** | `{sourceChannel}-{首条 sensorId}-{reportedAt}-{条数}` | **I-02 报文里没有 `eventId`** |
|
||
| **I-12** | `{sourceChannel}-{首条 rawRef}-{条数}` | **I-12 报文里没有 `eventId`**,`rawRef` 就是 `MD5(原始帧字节)` |
|
||
| I-05 | `{nodeId}-{首条 commandId}-{条数}` | 有 `commandId` |
|
||
| I-18 | **不带** | GET 拉取,重复拉天然幂等,靠租约去重 |
|
||
|
||
---
|
||
|
||
## 五、已知未定项(不藏着)
|
||
|
||
| 项 | 现状 | 何时定 |
|
||
|---|---|---|
|
||
| **错误响应里的 `code` 具体码值** | 只写了 `{code, message}` 结构,**没固定码值** | 待与业务层错误码表统一后回填。当前按 HTTP 段理解:400xx 请求非法/401xx 鉴权失败/429xx 限流 |
|
||
| **I-17 的请求体** | 只写了「本期 404」 | 二期启用诊断包时再补 |
|
||
| **`params` 的结构** | `additionalProperties: true`,随 `cmdType` 变 | 八种命令字的参数结构**不在本文件里**,由业务层命令模块保证;外包按 `cmdType` 做编码即可 |
|
||
|
||
---
|
||
|
||
## 六、对陈工和外包各说一句
|
||
|
||
**给陈工**:这份 YAML 可以生成 TS 类型,`openapi-generator -g typescript-fetch`。你前端**不用照 J-00 的表格手敲字段**,也不用维护第二份接口定义——生成一次即可,接口变了重生成。
|
||
|
||
**给外包(谢工)**:五个接口全在这里。各字段的 `description` 是给你的实现提示,重点是三处最容易做错的:
|
||
|
||
1. **I-02 / I-12 的幂等键不是 `eventId`**(报文里没这个字段),按对应 `components.parameters` 的说明算。
|
||
2. **I-18 是 GET,签名时 `sha256(body)` 取空串的 hash,`path` 含 query 一并算。**
|
||
3. **`rejectedDetail` 里出现的条目一律不重试**,只有整批 5xx 才重试。
|