# 阅读机/听书机 接口测试数据手册 > 配套文件:`阅读机数据表+接口设计-v1.md`、`015_train_test_data.sql` > 本文档中的「请求示例」全部使用 `015_train_test_data.sql` 已插入的真实数据,可直接照抄测试。 > > 基础地址:`http://localhost:8096/api/iot/v1`(端口按你实际改) > 数据前提:已执行 `015_train_test_data.sql`,且 `sys_user` 中存在 `userId=10001`、`status='0'`(正常)。 --- ## 0. 鉴权与测试顺序 | 接口 | 鉴权 | |---|---| | `POST /device/login` | 免鉴权(permitAll) | | `GET /book/list` | 免鉴权(已放开) | | `GET /book/download` | 免鉴权(已放开) | | 其余 6 个接口 | 需 `Authorization: Bearer `(来自登录返回) | > **登录接口有强制设备签名(`DeviceHmacFilter`)**:必须带 `X-Signature` + `X-Nonce` 请求头,且 body 的 `timestamp` 距今 ≤5 分钟,否则报「缺少设备签名头 / 签名校验失败 / 时间戳超时」。 > 本地联调不想算签名时,把 `application.yml` 的 `iot.hmac.enabled` 改为 `false`(重启后端)即可跳过验签;生产环境务必保持 `true`。 > 其余接口(Bearer 鉴权)不受该 Filter 影响。 **推荐顺序**:登录 → 复制 `data.accessToken` → 在 Swagger 点 Authorize 填 `Bearer `(或 curl 带 `-H 'Authorization: Bearer '`)→ 测其余接口。 --- ## 1. 设备学员登录 `POST /api/iot/v1/device/login` 请求(取自 015 设备 `READER-LIB-01` + 学员 `10001`): ```bash curl -X POST 'http://localhost:8096/api/iot/v1/device/login' \ -H 'Content-Type: application/json' \ -d '{ "deviceId":"READER-LIB-01", "userId":10001, "firmware":"1.4.2", "appVersion":"2.1.0", "timestamp":1767620000000, "nonce":"a1b2c3d4e5f6" }' ``` 预期响应: ```json { "code": 200, "msg": "操作成功", "data": { "accessToken": "", "sessionId": "<本次新建的业务会话ID>", "heartbeatInterval": 60 } } ``` --- ## 2. 设备注销 `POST /api/iot/v1/device/logout`(需 token) ```bash curl -X POST 'http://localhost:8096/api/iot/v1/device/logout' \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer ' \ -d '{"deviceId":"READER-LIB-01","reason":"REBOOT","timestamp":1767621000000,"nonce":"f6e5d4c3b2a1"}' ``` 预期:`{"data":{"result":true,"tokenRevoked":true}}` --- ## 3. 设备心跳 `POST /api/iot/v1/device/heartbeat`(需 token) ```bash curl -X POST 'http://localhost:8096/api/iot/v1/device/heartbeat' \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer ' \ -d '{"deviceId":"READER-LIB-01","status":"ONLINE","metrics":{"cpu":20,"mem":30,"diskFree":10240},"configVersion":"CV-17","personVersion":"PV-226118-7","timestamp":1767622000000,"nonce":"aa11bb22cc33"}' ``` 预期:`{"data":{"nextInterval":60,"commands":[]}}` --- ## 4. 阅读记录单条上报 `POST /api/iot/v1/reading/record`(需 token) **新 recordId(正常 ACCEPTED)** —— 用上架书 `BK-00012/BV-3`: ```bash curl -X POST 'http://localhost:8096/api/iot/v1/reading/record' \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer ' \ -d '{ "deviceId":"READER-LIB-01", "recordId":"RD-TEST-0001", "bookId":"BK-00012", "bookVersion":"BV-3", "mode":"read", "recordType":"END", "startTime":1767620000000, "endTime":1767622400000, "durationSec":1420, "pageFrom":12, "pageTo":46, "progress":0.35 }' ``` 预期:`{"data":{"result":"ACCEPTED","studyMinutes":23,"totalMinutes":<累计/60>}}` > 重复发送同一个 `recordId=RD-TEST-0001` → 返回 `{"result":"DUPLICATE",...}`(幂等)。 --- ## 5. 阅读记录批量上报 `POST /api/iot/v1/reading/record/batch`(需 token) ```bash curl -X POST 'http://localhost:8096/api/iot/v1/reading/record/batch' \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer ' \ -d '{ "deviceId":"READER-LIB-01", "batchId":"BATCH-RD-001", "timestamp":1767622500000, "nonce":"b2b3c4d5e6f7", "records":[ {"recordId":"RD-TEST-0002","bookId":"BK-00013","bookVersion":"BV-2","mode":"read","recordType":"END","startTime":1767620000000,"endTime":1767622400000,"durationSec":980,"pageFrom":5,"pageTo":22,"progress":0.18} ] }' ``` 预期:`{"data":{"itemResults":[{"recordId":"RD-TEST-0002","result":"ACCEPTED"}]}}` --- ## 6. 获取学员阅读汇总与续读进度 `GET /api/iot/v1/reading/progress`(需 token) ```bash curl 'http://localhost:8096/api/iot/v1/reading/progress?deviceId=READER-LIB-01' \ -H 'Authorization: Bearer ' ``` 预期(基于 015 已插入的进度/上报数据;需存在一个 `ACTIVE` 会话,登录后即有): ```json { "data": { "userId": 10001, "totalReadSeconds": 2400, "totalAudioSeconds": 600, "bookRecords": [ { "bookId": "BK-00012", "bookVersion": "BV-3", "mainProgress": {"mode":"read","pageNo":46,"sentenceIndex":null,"progress":0.35,"updateTime":1767622400000}, "alternateProgressList": [ {"mode":"read","bookVersion":"BV-2","pageNo":22,"sentenceIndex":null,"progress":0.18,"createTime":1767610000000} ] }, { "bookId": "BK-00012", "bookVersion": "BV-3", "mainProgress": {"mode":"audio","pageNo":120,"sentenceIndex":88,"progress":0.50,"updateTime":1767622500000}, "alternateProgressList": [] }, { "bookId": "BK-00013", "bookVersion": "BV-2", "mainProgress": {"mode":"read","pageNo":30,"sentenceIndex":null,"progress":0.20,"updateTime":1767622500000}, "alternateProgressList": [] } ] } } ``` > `totalReadSeconds`=1420+980(两条 read 上报累加);`totalAudioSeconds`=600(audio 上报)。 --- ## 7. 获取可阅读书籍列表 `GET /api/iot/v1/book/list`(免鉴权) ```bash # 全部上架书 curl 'http://localhost:8096/api/iot/v1/book/list?deviceId=READER-LIB-01' # 按分类过滤(只返回上架) curl 'http://localhost:8096/api/iot/v1/book/list?deviceId=READER-LIB-01&bookCategory=SAFETY' ``` 预期(`bookCategory` 省略时 `total=3`,只返回 `is_on_shelf=1` 的书;下架的 `BK-00012/BV-2`、`BK-00015` 不返回): ```json { "data": { "total": 3, "list": [ {"bookId":"BK-00012","bookTitle":"铁路房建施工安全手册","bookCategory":"SAFETY","bookVersion":"BV-3","fileSize":2048000,"fileMd5":"c4ca4238a0b923820dcc509a6f75849b","isOnShelf":true}, {"bookId":"BK-00013","bookTitle":"铁路电力安全工作规程","bookCategory":"REGULATION","bookVersion":"BV-2","fileSize":3072000,"fileMd5":"a1b2c3d4e5f60718293a4b5c6d7e8f90a","isOnShelf":true}, {"bookId":"BK-00014","bookTitle":"高铁客运服务英语","bookCategory":"LANGUAGE","bookVersion":"BV-1","fileSize":1536000,"fileMd5":"f0e1d2c3b4a5968778695a4b3c2d1e0f1","isOnShelf":true} ] } } ``` > `bookCategory=SAFETY` 时 `total=1`(仅 `BK-00012/BV-3`)。 --- ## 8. PDF 书籍下载 `GET /api/iot/v1/book/download`(免鉴权) ```bash # 上架书(正常) curl -i 'http://localhost:8096/api/iot/v1/book/download?deviceId=READER-LIB-01&bookId=BK-00012&bookVersion=BV-3' # 支持断点续传 curl -i -H 'Range: bytes=0-1023' 'http://localhost:8096/api/iot/v1/book/download?deviceId=READER-LIB-01&bookId=BK-00012&bookVersion=BV-3' ``` - 上架书:HTTP 200(或 Range 时 206),响应头带 `Accept-Ranges: bytes`、`Content-MD5`。 - 下架书:`bookId=BK-00012&bookVersion=BV-2`(is_on_shelf=0)→ 业务报错 `{"code":500,"msg":"图书已下架,禁止下载"}`。 - 不存在的版本:`bookId=BK-00015&bookVersion=BV-9` → `图书或版本不存在`。 > ⚠️ `download_url` 支持两种形态: > ① **相对路径**(如正式数据的 `/profile/upload/2026/10/21/铁路房建施工安全手册_20261021085854A001.pdf`,或测试用的 `/books/BK-00012/BV-3.pdf`)——后端自动在前面补上「后端 base URL(`scheme+host+port`,如 `http://localhost:8096`)」转成可访问的 http(s) 绝对地址后再流式返回; > ② **完整 http(s) 远程地址**(如 OSS)——直接拉取。 > 两种都会自动加 `Content-Type: application/pdf` 与 `Content-Disposition`(下载头),保证浏览器可访问。 > 相对路径由后端自身静态资源处理器提供文件:`/profile/**` 指向 RuoYi 上传根目录(文件须真实存在于该磁盘目录),`/books/**` 指向 `classpath:/static/books/`(测试 PDF 已放此处)。文件不存在则返回「文件不存在」。 --- ## 9. 下载完成回执上报 `POST /api/iot/v1/book/download/ack`(需 token) ```bash curl -X POST 'http://localhost:8096/api/iot/v1/book/download/ack' \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer ' \ -d '{ "deviceId":"READER-LIB-01", "bookId":"BK-00012", "bookVersion":"BV-3", "fileMd5":"c4ca4238a0b923820dcc509a6f75849b", "result":"SUCCESS", "errMsg":"", "timestamp":1767630000000, "nonce":"cc55dd66ee77" }' ``` 预期:`{"data":{"deviceReady":true,"recordId":<自增主键>}}` > `result` 可取 `SUCCESS`/`FAIL`(缺省 SUCCESS);落库 `train_book_download_record`。 --- ## 10. 边界 / 异常用例矩阵(均基于 015 数据) | 用例 | 操作 | 预期 | |---|---|---| | 禁用设备登录 | `device/login` 用 `deviceId=READER-LIB-03`(status=2) | `msg:"设备已禁用"` | | 重复上报幂等 | `reading/record` 用已存在的 `recordId=RD-20261020-0001` | `result:"DUPLICATE"` | | 冲突进度(REPLACED 仅补交) | 用 `deviceId=READER-LIB-02`(其会话 `SES-20261019-0001` 是 REPLACED)发 `reading/record` 新 recordId | `result:"CONFLICT_SAVED"`,写入 `train_reading_alternate_progress` | | 下架书被过滤 | `book/list` 不含 `BK-00012/BV-2`、`BK-00015` | `total=3`(仅上架) | | 下架书禁止下载 | `book/download?bookId=BK-00012&bookVersion=BV-2` | 业务错误「图书已下架,禁止下载」 | | REPLACED 禁止进度查询 | `reading/progress?deviceId=READER-LIB-02`(需先有一个有效 token) | `msg:"当前会话状态不允许查询进度(被替换设备仅允许补交上报)"` | | 版本强绑定 | 进度查询中 `BK-00012` 的 `read` 主进度 `BV-3` 与备选 `BV-2` 不混用页码 | 各自返回对应版本页码 | --- ## 11. 已插入测试数据速查(源自 015) **设备 `train_iot_device`** - `READER-LIB-01` status=1(正常)、`READER-LIB-02` status=1、`READER-LIB-03` status=2(禁用) **图书 `train_book_snapshot`**(is_on_shelf) - `BK-00012/BV-3` SAFETY 上架 md5=`c4ca4238a0b923820dcc509a6f75849b` - `BK-00012/BV-2` SAFETY **下架** - `BK-00013/BV-2` REGULATION 上架 md5=`a1b2c3d4e5f60718293a4b5c6d7e8f90a` - `BK-00014/BV-1` LANGUAGE 上架 md5=`f0e1d2c3b4a5968778695a4b3c2d1e0f1` - `BK-00015/BV-1` REGULATION **下架** **会话 `train_user_session`** - `SES-20261020-0001` user=10001 device=READER-LIB-01 status=1 ACTIVE - `SES-20261019-0001` user=10001 device=READER-LIB-02 status=2 REPLACED(用于冲突用例) **主进度 `train_reading_current_progress`**:10001 的 `BK-00012/read/BV-3`(46,0.35)、`BK-00012/audio/BV-3`(120,88,0.50)、`BK-00013/read/BV-2`(30,0.20) **备选进度 `train_reading_alternate_progress`**:10001 `BK-00012/read/BV-2`(22,0.18) **上报日志 `train_reading_duration_log`**:`RD-20261020-0001`(ACCEPTED)、`RD-20261020-0002`(ACCEPTED)、`RD-20261019-0001`(CONFLICT_SAVED) **下载回执 `train_book_download_record`**:`BK-00012/BV-3` SUCCESS、`BK-00013/BV-2` SUCCESS、`BK-00014/BV-1` FAIL(errMsg=存储空间不足)