Files
hepu/docs/contract/Mock样例.md
T
aomin a7605ef8b8 chore(repo): 导入 M-Z 业务底座 V1.9 基线并建立仓库规范
按周工交付的 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),不做归一化以免整树摘要失效。
2026-11-13 17:32:37 +08:00

4.7 KiB
Raw Blame History

Mock 样例(可直接拿去打)

六组报文全部取自 2026-11-09 20:21 的真实一次运行,不是手编的示例。 对应证据:06_统一闭环题自测证据/05_SOS链路.json


1. I-01 事件上报 · 请求

POST /internal/iot/v1/events/batch HTTP/1.1
Content-Type: application/json
X-Idempotency-Key: batch-20261109-2021-001
{
  "sourceChannel": "OWN_GW",
  "events": [{
    "eventId": "EVT-SOS-3B9B2CDB9CD4",
    "sensorId": "3F7A9C23",
    "sourceChannel": "OWN_GW",
    "eventType": "SOS_PRESS",
    "deviceTime": null,
    "receivedAt": "2026-11-09T20:21:12+08:00",
    "tsSource": 3,
    "protocolVersion": "gw-1.2",
    "seq": 16872,
    "payload": {"deviceTypeRaw": 5, "rawEventCode": 1, "batteryMv": 3610},
    "rawRef": "e1abbc2d6515f118ac9e7e113c68d677"
  }]
}

三个刻意的点

  • deviceTime: null —— 433 帧没有时间字段,按 J-00 必须传 null,不许拿 receivedAt 顶替
  • tsSource: 3 —— 网关打戳,界面不能显示成"发生时间"
  • payload.deviceTypeRaw: 5 —— 任何帧都必须带,缺了整条拒收

2. I-01 响应

{"code":0,"msg":"ok","data":{
  "accepted":1, "rejected":0, "duplicated":0, "unregistered":0, "rejectedDetail":[]}}

3. I-01 失败样例(四种)

// ① 缺 deviceTypeRaw
{"code":0,"data":{"accepted":0,"rejected":1,"rejectedDetail":[
  {"eventId":"EVT-BAD-01","reason":"SCHEMA_INVALID"}]}}

// ② SN 与通道不符(OWN_GW 传带前缀的 SN)
{"code":0,"data":{"accepted":0,"rejected":1,"rejectedDetail":[
  {"eventId":"EVT-BAD-02","reason":"SENSOR_ID_INVALID"}]}}

// ③ tsSource 越界
{"code":0,"data":{"accepted":0,"rejected":1,"rejectedDetail":[
  {"eventId":"EVT-BAD-03","reason":"SCHEMA_INVALID"}]}}

// ④ 批级通道非法 → 整批 400
{"code":400,"msg":"sourceChannel 非法:H_MQTT"}

4. I-02 设备状态快照

{
  "sourceChannel": "OWN_GW",
  "items": [{
    "sensorId": "3F7A9C23",
    "sourceChannel": "OWN_GW",
    "batteryMv": 3610,
    "csq": 22,
    "online": true,
    "reportedAt": "2026-11-09T20:21:10+08:00"
  }]
}

5. I-12 原始报文留档

{
  "sourceChannel": "OWN_GW",
  "frames": [{
    "sensorId": "3F7A9C23",
    "rawRef": "e1abbc2d6515f118ac9e7e113c68d677",
    "frameHex": "A55A213F7A9C2350100001000000E8B4",
    "frameLen": 17,
    "receivedAt": "2026-11-09T20:21:12+08:00"
  }]
}

rawRef 与 I-01 必须同一种算法(MD5 32 位或 SHA-256 64 位),否则回指链断。

6. I-18 / I-05 下行命令

编号按 J-00 V1.8 §三接口表:领取是 I-18、回执是 I-05(原写 I-20/I-21,已作废)。

// I-18 拉取(长轮询,wait 最长 30s)
GET /internal/iot/v1/commands/pending?nodeId=iot-node-1&wait=30
{"code":0,"data":{"nodeId":"iot-node-1","hasMore":false,"commands":[
  {"commandId":"CMD17942268075877","gatewayId":"GW-0001","targetId":"3F7A9C23",
   "cmdType":"time_sync","deliveryMode":"REALTIME",
   "params":{"ts":"2026-11-09T20:21:12+08:00"},
   "leaseExpireAt":"2026-11-09T20:22:12+08:00","expireAt":"2026-11-12T20:21:12+08:00",
   "retryCount":0}]}}

// I-05 回执(result 五值;resultCode 是标准码,不是厂家原码)
POST /internal/iot/v1/commands/result
{"nodeId":"iot-node-1","results":[
  {"commandId":"CMD17942268075877","result":"ACKED","resultCode":"0","resultMsg":"OK",
   "ackAt":"2026-11-09T20:21:14+08:00","rawResultCode":"00"}]}
{"code":0,"msg":"ok"}

7. 服务端推给家属端(SSE)

event: alarm
data:{"type":"alarm","alarmId":2796,"alarmNo":"AL20261109202112922",
      "householdId":9001,"level":"S1","tier":"T1","ruleKey":"SOS",
      "title":"紧急呼叫","content":"老人按下紧急按钮,请立即联系",
      "deviceName":"床头SOS","deviceSn":"3F7A9C23",
      "triggerAt":"2026-11-09T20:21:12","delayed":false}

8. 怎么用这些样例自测

# 登录拿 token(账号由 DemoDataInitializer 在空库首次启动时生成)
curl -X POST http://127.0.0.1:8080/api/v1/auth/wx/password-login \
  -H "Content-Type: application/json" \
  -d '{"phone":"13800000000","password":"123456"}'

# 打一条 SOS(把上面第 1 节的 body 存成 sos.json)
curl -X POST http://127.0.0.1:8080/internal/iot/v1/events/batch \
  -H "Content-Type: application/json" \
  -H "X-Idempotency-Key: batch-local-001" \
  -d @sos.json

# 查告警是否生成
curl "http://127.0.0.1:8080/api/v1/alarms?size=3" -H "Authorization: Bearer <token>"

预期:1 秒内家属端 SSE 收到 event: alarm,列表首条是刚才那条 SOS。

注意:sensorId 必须已经入库(后台「设备入库」录入或跑 03 的虚构台账数据), 否则会返回 unregistered,不建档也不告警 —— 这是 A 方案的刻意设计。