# SmartLearning 接口文档 > 服务默认地址:`http://{host}:{port}`(默认 `http://0.0.0.0:9500`) > 交互式 API 文档:启动服务后访问 `/docs` > 协议版本:HTTP API v1 / MQTT snlp/v1 --- ## 目录 - [一、通用说明](#一通用说明) - [二、HTTP 接口](#二http-接口) - [2.1 系统](#21-系统) - [2.2 静态资源](#22-静态资源) - [2.3 场景识别](#23-场景识别) - [2.4 对话流](#24-对话流) - [2.5 课程与任务](#25-课程与任务) - [2.6 设备初始化](#26-设备初始化) - [2.7 设备管理](#27-设备管理) - [三、MQTT 接口](#三mqtt-接口) - [3.1 Topic 规则](#31-topic-规则) - [3.2 消息通用格式](#32-消息通用格式) - [3.3 终端上报(rpt)](#33-终端上报rpt) - [3.4 云端下发(cmd)](#34-云端下发cmd) - [3.5 服务端自动下发流程](#35-服务端自动下发流程) - [四、接口速查表](#四接口速查表) --- ## 一、通用说明 ### 1.1 Base URL | 环境 | 地址 | |------|------| | 本地开发 | `http://localhost:9500` | | 生产部署 | 由 `.env` 中 `BASE_URL` 配置,终端下载资源须使用该地址 | ### 1.2 请求头 JSON 接口统一使用: ``` Content-Type: application/json ``` SSE 对话流接口返回: ``` Content-Type: text/event-stream ``` ### 1.3 错误响应 FastAPI 标准错误格式: ```json { "detail": "Course not found: course99" } ``` 常见 HTTP 状态码: | 状态码 | 说明 | |--------|------| | 200 | 成功 | | 400 | 请求参数错误 | | 404 | 资源不存在 | | 422 | 请求体校验失败 | --- ## 二、HTTP 接口 ### 2.1 系统 #### GET /health 健康检查,查看服务与 MQTT 连接状态。 **请求示例:** ```bash curl http://localhost:9500/health ``` **响应示例:** ```json { "status": "ok", "mqtt_connected": true } ``` | 字段 | 类型 | 说明 | |------|------|------| | status | string | 服务状态,固定 `"ok"` | | mqtt_connected | boolean | MQTT Broker 是否已连接 | --- ### 2.2 静态资源 #### GET /static/{path} 托管 `data/` 目录下的课程文件,供终端 HTTP 下载。 **URL 规则:** ``` {BASE_URL}/static/{相对路径} ``` **示例:** | 资源 | URL | |------|-----| | 课程索引 | `http://localhost:9500/static/courses/index.json` | | 课程配置 | `http://localhost:9500/static/courses/course01/course.json` | | 课程任务包 | `http://localhost:9500/static/courses/course01/courseTask.json` | | 音频 | `http://localhost:9500/static/courses/course01/chapter01/resources/001.mp3` | | 封面 | `http://localhost:9500/static/courses/course01/chapter01/resources/001.png` | **请求示例:** ```bash curl -O http://localhost:9500/static/courses/course01/courseTask.json ``` **响应:** 对应文件的二进制/JSON 内容。 --- ### 2.3 场景识别 #### POST /api/v1/scene/analyze 根据设备上下文进行场景识别,返回问候策略与建议意图。 **请求体:** | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | timestamp | int | 是 | Unix 时间戳 | | audio_blob | string | 是 | Base64 音频或占位符 | | device_id | string | 是 | 设备 ID | | context_hints | object | 否 | 场景提示,如 `{"scene":"dining","confidence":0.9}` | **请求示例:** ```bash curl -X POST http://localhost:9500/api/v1/scene/analyze \ -H "Content-Type: application/json" \ -d '{ "timestamp": 1718365200, "audio_blob": "", "device_id": "SN-88293021", "context_hints": { "scene": "dining", "confidence": 0.9 } }' ``` **响应示例:** ```json { "scene": "用餐", "confidence": 0.9, "greeting_strategy": "contextual", "suggested_intent": "What are you having for dinner?" } ``` | 字段 | 类型 | 说明 | |------|------|------| | scene | string | 场景名称 | | confidence | float | 置信度 0~1 | | greeting_strategy | string | `"contextual"` 场景问候 / `"generic"` 通用问候(置信度 < 0.7) | | suggested_intent | string | 建议的开场白或提问 | **低置信度响应示例:** ```json { "scene": "通勤", "confidence": 0.62, "greeting_strategy": "generic", "suggested_intent": "Hello! How can I help you practice English today?" } ``` --- ### 2.4 对话流 #### GET /api/v1/dialogue/stream SSE(Server-Sent Events)流式返回教学对话内容。 **Query 参数:** | 参数 | 类型 | 必填 | 默认值 | 说明 | |------|------|------|--------|------| | dialogue_id | string | 否 | 自动生成 UUID | 对话 ID | | user_text | string | 否 | — | 用户回复文本(ASR 结果) | | scene_prompt | string | 否 | — | 场景开场白(来自 scene/analyze) | | difficulty | string | 否 | A2 | CEFR 难度等级 | **请求示例:** ```bash # 仅开场问候 curl -N "http://localhost:9500/api/v1/dialogue/stream?scene_prompt=What%20are%20you%20having%20for%20dinner?&difficulty=A2" # 含用户回复的反馈 curl -N "http://localhost:9500/api/v1/dialogue/stream?user_text=Good%20morning.&difficulty=A2" ``` **响应格式(SSE):** ``` event: message data: {"dialogue_id":"a1b2c3d4-...","text":"What are you having for dinner?","audio_url":null,"intent_type":"greeting","difficulty_level":"A2"} event: message data: {"dialogue_id":"a1b2c3d4-...","text":"Great job! Your sentence looks correct.","audio_url":null,"intent_type":"praise","difficulty_level":"A2"} event: message data: {"dialogue_id":"a1b2c3d4-...","text":"What would you do differently next time?","audio_url":null,"intent_type":"question","difficulty_level":"A2"} event: done data: {"dialogue_id":"a1b2c3d4-..."} ``` **message 事件 data 字段:** | 字段 | 类型 | 说明 | |------|------|------| | dialogue_id | string | 对话 ID | | text | string | 对话文本 | | audio_url | string \| null | 音频 URL(当前为 null) | | intent_type | string | `greeting` / `praise` / `correction` / `question` | | difficulty_level | string | 难度等级 | --- ### 2.5 课程与任务 #### GET /api/v1/courses 获取已发布课程索引(对应 `data/courses/index.json`)。 **请求示例:** ```bash curl http://localhost:9500/api/v1/courses ``` **响应示例:** ```json { "courses": [ { "course_id": "course01", "config": "courses/course01/course.json" }, { "course_id": "course02", "config": "courses/course02/course.json" } ] } ``` --- #### GET /api/v1/courses/{course_id} 获取单门课程详情(章节与视频列表)。 **请求示例:** ```bash curl http://localhost:9500/api/v1/courses/course01 ``` **响应示例:** ```json { "course_id": "course01", "course_name": "小智学习课程01", "description": "课程01", "chapters": [ { "chapter_id": "chapter01", "chapter_name": "第一章", "videos": [ { "id": "001", "title": "课程001", "path": "courses/course01/chapter01/resources/001.mp3" } ] } ] } ``` --- #### GET /api/v1/courses/base-manifest 预览全部基础课程同步包(MQTT 下发前的完整 payload,`url` 已补全)。 **请求示例:** ```bash curl http://localhost:9500/api/v1/courses/base-manifest ``` **响应示例:** ```json { "scope": "base", "course_count": 2, "courses": [ { "version": "1.0", "course_id": "course01", "course_name": "小智学习课程01", "description": "课程01终端初始化任务:下载章节视频并更新本地资源清单", "assets": [ { "url": "http://localhost:9500/static/courses/course01/chapter01/resources/001.mp3", "md5": "...", "title": "第一章 课程001", "target_path": "courses/course01/chapter01/resources/001.mp3", "cover_url": "http://localhost:9500/static/courses/course01/chapter01/resources/001.png", "cover_target_path": "courses/course01/chapter01/resources/001.png" } ] } ] } ``` --- #### GET /api/v1/courses/{course_id}/manifest 获取单门课程的同步包(`url` / `cover_url` 已补全为完整 HTTP 地址)。 **请求示例:** ```bash curl http://localhost:9500/api/v1/courses/course01/manifest ``` **响应示例:** ```json { "version": "1.0", "course_id": "course01", "course_name": "小智学习课程01", "description": "课程01终端初始化任务:下载章节视频并更新本地资源清单", "assets": [ { "url": "http://localhost:9500/static/courses/course01/chapter01/resources/001.mp3", "md5": "...", "title": "第一章 课程001", "target_path": "courses/course01/chapter01/resources/001.mp3", "cover_url": "http://localhost:9500/static/courses/course01/chapter01/resources/001.png", "cover_target_path": "courses/course01/chapter01/resources/001.png" } ] } ``` --- #### GET /api/v1/courses/{course_id}/tasks 获取原始 `courseTask.json`(本地文件内容,`url` 为相对路径)。 **请求示例:** ```bash curl http://localhost:9500/api/v1/courses/course01/tasks ``` **响应示例:** ```json { "version": "1.0", "course_id": "course01", "course_name": "小智学习课程01", "description": "课程01终端初始化任务:下载章节视频并更新本地资源清单", "assets": [ { "url": "courses/course01/chapter01/resources/001.mp3", "md5": "...", "title": "第一章 课程001", "target_path": "courses/course01/chapter01/resources/001.mp3", "cover_url": "courses/course01/chapter01/resources/001.png", "cover_target_path": "courses/course01/chapter01/resources/001.png" } ] } ``` --- #### GET /api/v1/tasklist 获取设备开机初始化任务清单(`data/tasklist.json`)。 **请求示例:** ```bash curl http://localhost:9500/api/v1/tasklist ``` **响应示例:** ```json { "version": "1.0", "description": "终端设备初始化任务清单,开机后由服务端下发执行", "tasks": [ { "task_id": "init_001", "name": "同步系统时钟", "action": "SET_CLOCK", "channel": "ctl", "ttl": 60, "payload": { "ts": 0 } }, { "task_id": "init_002", "name": "设置默认音量", "action": "SET_VOLUME", "channel": "ctl", "ttl": 60, "payload": { "level": 50 } }, { "task_id": "init_003", "name": "唤醒设备", "action": "SET_POWER", "channel": "ctl", "ttl": 60, "payload": { "state": "wake" } } ] } ``` --- ### 2.6 设备初始化 #### POST /api/v1/devices/init 手动触发设备完整初始化(基础 ctl 指令 + 全部基础课程 SYNC_COURSE_TASK)。 **请求体:** | 字段 | 类型 | 必填 | 默认值 | 说明 | |------|------|------|--------|------| | device_id | string | 是 | — | 设备 ID | | force | boolean | 否 | false | 强制重新初始化(忽略已初始化记录) | | course_id | string | 否 | — | 本接口不使用 | **请求示例:** ```bash curl -X POST http://localhost:9500/api/v1/devices/init \ -H "Content-Type: application/json" \ -d '{ "device_id": "SN-88293021", "force": true }' ``` **响应示例(成功下发):** ```json { "device_id": "SN-88293021", "commands": [ { "header": { "version": "1.0", "device_id": "SN-88293021", "req_id": "8f932149-719c-4464-99be-66dfcfa14f29", "timestamp": 1718365200, "ttl": 60 }, "action": "SET_CLOCK", "payload": { "ts": 1718365200 } }, { "header": { "version": "1.0", "device_id": "SN-88293021", "req_id": "e1c1de2a-3067-417d-b533-67c9a9827a8e", "timestamp": 1718365201, "ttl": 7200 }, "action": "SYNC_COURSE_TASK", "payload": { "version": "1.0", "course_id": "course01", "course_name": "小智学习课程01", "assets": [] } } ], "status": "dispatched" } ``` **响应示例(已初始化且未 force):** ```json { "device_id": "SN-88293021", "commands": [], "status": "already_initialized" } ``` | status 值 | 说明 | |-----------|------| | dispatched | 指令已下发 | | already_initialized | 设备已在初始化记录中,跳过 | --- #### POST /api/v1/devices/course-init 手动触发单门课程资源同步(下发 1 条 `SYNC_COURSE_TASK`)。 **请求体:** | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | device_id | string | 是 | 设备 ID | | course_id | string | 是 | 课程 ID,如 `course01` | **请求示例:** ```bash curl -X POST http://localhost:9500/api/v1/devices/course-init \ -H "Content-Type: application/json" \ -d '{ "device_id": "SN-88293021", "course_id": "course01" }' ``` **响应示例:** ```json { "device_id": "SN-88293021", "course_id": "course01", "commands": [ { "header": { "version": "1.0", "device_id": "SN-88293021", "req_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "timestamp": 1718365300, "ttl": 7200 }, "action": "SYNC_COURSE_TASK", "payload": { "version": "1.0", "course_id": "course01", "course_name": "小智学习课程01", "description": "课程01终端初始化任务:下载章节视频并更新本地资源清单", "assets": [ { "url": "http://localhost:9500/static/courses/course01/chapter01/resources/001.mp3", "md5": "...", "title": "第一章 课程001", "target_path": "courses/course01/chapter01/resources/001.mp3", "cover_url": "http://localhost:9500/static/courses/course01/chapter01/resources/001.png", "cover_target_path": "courses/course01/chapter01/resources/001.png" } ] } } ], "status": "dispatched" } ``` --- ### 2.7 设备管理 #### GET /api/v1/devices 获取当前在线设备列表(来自 MQTT `rpt/state` 上报)。 **请求示例:** ```bash curl http://localhost:9500/api/v1/devices ``` **响应示例:** ```json { "devices": [ { "device_id": "SN-88293021", "cpu": 15.0, "mem": 45.0, "battery": 80.0, "net": "wifi", "mode": "idle", "last_seen": 1718365205 } ] } ``` --- #### GET /api/v1/devices/initialized 获取已完成初始化的设备记录。 **请求示例:** ```bash curl http://localhost:9500/api/v1/devices/initialized ``` **响应示例:** ```json { "devices": { "SN-88293021": { "initialized_at": 1718365200, "source": "auto", "mode": "idle", "courses_synced": true, "courses_sync_at": 1718365201, "courses_sync_count": 2 } } } ``` --- #### GET /api/v1/devices/{device_id} 获取单个设备最新状态。 **请求示例:** ```bash curl http://localhost:9500/api/v1/devices/SN-88293021 ``` **响应示例:** ```json { "device_id": "SN-88293021", "cpu": 15.0, "mem": 45.0, "battery": 80.0, "net": "wifi", "mode": "idle", "last_seen": 1718365205 } ``` **404 响应:** ```json { "detail": "Device not found" } ``` --- #### GET /api/v1/devices/{device_id}/init-status 查询设备初始化状态。 **请求示例:** ```bash curl http://localhost:9500/api/v1/devices/SN-88293021/init-status ``` **响应示例(已初始化):** ```json { "device_id": "SN-88293021", "initialized": true, "record": { "initialized_at": 1718365200, "source": "auto", "mode": "idle", "courses_synced": true, "courses_sync_at": 1718365201, "courses_sync_count": 2 } } ``` **响应示例(未初始化):** ```json { "device_id": "SN-88293021", "initialized": false, "record": null } ``` --- #### DELETE /api/v1/devices/initialized/{device_id} 清除设备初始化记录,下次上报 `rpt/state` 时将重新触发自动初始化。 **请求示例:** ```bash curl -X DELETE http://localhost:9500/api/v1/devices/initialized/SN-88293021 ``` **响应示例:** ```json { "device_id": "SN-88293021", "status": "removed" } ``` --- #### GET /api/v1/devices/{device_id}/acks 获取设备指令回执历史。 **请求示例:** ```bash curl http://localhost:9500/api/v1/devices/SN-88293021/acks ``` **响应示例:** ```json { "device_id": "SN-88293021", "acks": [ { "req_id": "8f932149-719c-4464-99be-66dfcfa14f29", "status": "success", "error_code": 0 } ] } ``` --- #### GET /api/v1/devices/{device_id}/events 获取设备事件与进度上报历史。 **请求示例:** ```bash curl http://localhost:9500/api/v1/devices/SN-88293021/events ``` **响应示例:** ```json { "device_id": "SN-88293021", "events": [ { "event": "asr_result", "text": "Good morning", "confidence": 0.95, "_type": "event" }, { "task_id": "uuid", "status": "downloading", "progress": 45, "info": "Downloading assets...", "_type": "progress" } ] } ``` --- #### POST /api/v1/devices/{device_id}/cmd 手动向设备下发实时控制指令(MQTT `cmd/ctl`)。 **请求体:** ```json { "header": { "device_id": "SN-88293021", "timestamp": 1718365200, "ttl": 60 }, "action": "SET_VOLUME", "payload": { "level": 80 } } ``` **请求示例:** ```bash curl -X POST http://localhost:9500/api/v1/devices/SN-88293021/cmd \ -H "Content-Type: application/json" \ -d '{ "header": { "device_id": "SN-88293021", "timestamp": 1718365200, "ttl": 60 }, "action": "SET_VOLUME", "payload": { "level": 80 } }' ``` **响应示例(返回实际下发的完整指令):** ```json { "header": { "version": "1.0", "device_id": "SN-88293021", "req_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "timestamp": 1718365200, "ttl": 60 }, "action": "SET_VOLUME", "payload": { "level": 80 } } ``` --- #### POST /api/v1/devices/{device_id}/task 手动向设备下发异步任务指令(MQTT `cmd/task`)。 **请求示例:** ```bash curl -X POST http://localhost:9500/api/v1/devices/SN-88293021/task \ -H "Content-Type: application/json" \ -d '{ "header": { "device_id": "SN-88293021", "timestamp": 1718365200, "ttl": 7200 }, "action": "SYNC_COURSE_TASK", "payload": { "version": "1.0", "course_id": "course01", "course_name": "小智学习课程01", "assets": [] } }' ``` **响应示例:** ```json { "header": { "version": "1.0", "device_id": "SN-88293021", "req_id": "550e8400-e29b-41d4-a716-446655440000", "timestamp": 1718365200, "ttl": 7200 }, "action": "SYNC_COURSE_TASK", "payload": { "version": "1.0", "course_id": "course01", "course_name": "小智学习课程01", "assets": [] } } ``` --- ## 三、MQTT 接口 ### 3.1 Topic 规则 ``` snlp/v1/{device_id}/{direct}/{msg_type} ``` | 方向 | Topic | QoS | 说明 | |------|-------|-----|------| | 终端 → 云 | `snlp/v1/{device_id}/rpt/state` | 0 | 心跳/状态上报 | | 终端 → 云 | `snlp/v1/{device_id}/rpt/event` | 0 | 事件上报 | | 终端 → 云 | `snlp/v1/{device_id}/rpt/ack` | 1 | 指令执行回执 | | 终端 → 云 | `snlp/v1/{device_id}/rpt/progress` | 0 | 异步任务进度 | | 终端 → 云 | `snlp/v1/{device_id}/rpt/log` | 0 | 系统日志 | | 云 → 终端 | `snlp/v1/{device_id}/cmd/ctl` | 1 | 实时控制指令 | | 云 → 终端 | `snlp/v1/{device_id}/cmd/task` | 1 | 异步任务指令 | **参数说明:** | 参数 | 说明 | |------|------| | v1 | 协议版本号 | | device_id | 设备唯一 ID,不可修改 | | direct | `rpt`(终端上报)/ `cmd`(云端下发) | | msg_type | `state` / `event` / `ack` / `progress` / `log` / `ctl` / `task` | --- ### 3.2 消息通用格式 #### 云端下发(cmd/ctl、cmd/task) ```json { "header": { "version": "1.0", "device_id": "SN-88293021", "req_id": "550e8400-e29b-41d4-a716-446655440000", "timestamp": 1718365200, "ttl": 3600 }, "action": "ACTION_TYPE", "payload": {} } ``` | header 字段 | 类型 | 说明 | |-------------|------|------| | version | string | 协议语义版本,固定 `"1.0"` | | device_id | string | 目标设备 ID | | req_id | string | 指令唯一 ID(UUID),终端 ack 时须回传 | | timestamp | int | Unix 时间戳 | | ttl | int | 指令有效期(秒) | #### 终端上报(rpt/*) ```json { "header": { "device_id": "SN-88293021", "timestamp": 1718365205 }, "msg_type": "state", "payload": {} } ``` --- ### 3.3 终端上报(rpt) #### rpt/state — 心跳/状态 **Topic:** `snlp/v1/{device_id}/rpt/state` **发布示例(终端 → 云):** ```json { "header": { "device_id": "SN-88293021", "timestamp": 1718365205 }, "msg_type": "state", "payload": { "cpu": 15, "mem": 45, "battery": 80, "net": "wifi", "mode": "idle" } } ``` **服务端行为:** - 更新设备在线状态 - 若设备未初始化且 MQTT 已连接 → 自动触发完整初始化流程 **mode 常见值:** `idle` / `dialogue` / `translate` / `study` / `sleep` --- #### rpt/event — 事件上报 **Topic:** `snlp/v1/{device_id}/rpt/event` **发布示例:** ```json { "header": { "device_id": "SN-88293021", "timestamp": 1718365210 }, "msg_type": "event", "payload": { "event": "asr_result", "text": "Good morning", "confidence": 0.95 } } ``` **常见 event 类型:** | event | 说明 | |-------|------| | wake | 唤醒触发 | | sleep | 休眠触发 | | asr_result | ASR 识别结果 | | alarm | 异常报警 | --- #### rpt/ack — 指令回执 **Topic:** `snlp/v1/{device_id}/rpt/ack` **发布示例:** ```json { "header": { "device_id": "SN-88293021", "timestamp": 1718365215 }, "msg_type": "ack", "payload": { "req_id": "550e8400-e29b-41d4-a716-446655440000", "status": "success", "error_code": 0 } } ``` | payload 字段 | 说明 | |--------------|------| | req_id | 对应下发指令的 req_id | | status | `success` / `failed` | | error_code | 0 表示成功,非 0 为错误码 | --- #### rpt/progress — 任务进度 **Topic:** `snlp/v1/{device_id}/rpt/progress` **发布示例:** ```json { "header": { "device_id": "SN-88293021", "timestamp": 1718365220 }, "msg_type": "progress", "payload": { "task_id": "550e8400-e29b-41d4-a716-446655440000", "status": "downloading", "progress": 45, "info": "Downloading courses/course01/chapter01/resources/001.mp3" } } ``` --- #### rpt/log — 系统日志 **Topic:** `snlp/v1/{device_id}/rpt/log` **发布示例:** ```json { "header": { "device_id": "SN-88293021", "timestamp": 1718365230 }, "msg_type": "log", "payload": { "level": "error", "module": "audio", "message": "Microphone init failed" } } ``` --- ### 3.4 云端下发(cmd) #### cmd/ctl — 实时控制指令 **Topic:** `snlp/v1/{device_id}/cmd/ctl` **Action 列表:** | Action | payload 示例 | 说明 | |--------|-------------|------| | SET_VOLUME | `{"level": 50}` | 设置音量 0–100 | | SET_POWER | `{"state": "wake"}` | 唤醒 / `sleep` 休眠 | | SET_CLOCK | `{"ts": 1718365200}` | 同步 Unix 时间戳 | | PLAY_MEDIA | `{"path": "courses/course01/chapter01/resources/001.mp3", "volume": 50, "loop": false}` | 播放本地媒体 | | STOP_MEDIA | `{}` | 停止播放 | | SET_ALARM | `{"id": "alarm_01", "time": "07:00", "repeat": [1,2,3,4,5], "action": "add"}` | 设置闹钟 | | UNSET_ALARM | `{"id": "alarm_01"}` | 删除闹钟 | | REBOOT | `{"delay": 5}` | 延时重启 | **SET_CLOCK 下发示例:** ```json { "header": { "version": "1.0", "device_id": "SN-88293021", "req_id": "8f932149-719c-4464-99be-66dfcfa14f29", "timestamp": 1718365200, "ttl": 60 }, "action": "SET_CLOCK", "payload": { "ts": 1718365200 } } ``` **SET_VOLUME 下发示例:** ```json { "header": { "version": "1.0", "device_id": "SN-88293021", "req_id": "34dc4157-9e52-4a99-bdad-b3daf665df11", "timestamp": 1718365200, "ttl": 60 }, "action": "SET_VOLUME", "payload": { "level": 50 } } ``` **SET_POWER 下发示例:** ```json { "header": { "version": "1.0", "device_id": "SN-88293021", "req_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "timestamp": 1718365200, "ttl": 60 }, "action": "SET_POWER", "payload": { "state": "wake" } } ``` --- #### cmd/task — 异步任务指令 **Topic:** `snlp/v1/{device_id}/cmd/task` **当前实现使用的 Action:** | Action | 说明 | |--------|------| | SYNC_COURSE_TASK | 下发完整课程资源包(courseTask.json 格式),终端按 assets 批量下载 | **SYNC_COURSE_TASK 下发示例:** ```json { "header": { "version": "1.0", "device_id": "SN-88293021", "req_id": "e1c1de2a-3067-417d-b533-67c9a9827a8e", "timestamp": 1718365201, "ttl": 7200 }, "action": "SYNC_COURSE_TASK", "payload": { "version": "1.0", "course_id": "course01", "course_name": "小智学习课程01", "description": "课程01终端初始化任务:下载章节视频并更新本地资源清单", "assets": [ { "url": "http://your-server:9500/static/courses/course01/chapter01/resources/001.mp3", "md5": "...", "title": "第一章 课程001", "target_path": "courses/course01/chapter01/resources/001.mp3", "cover_url": "http://your-server:9500/static/courses/course01/chapter01/resources/001.png", "cover_target_path": "courses/course01/chapter01/resources/001.png" }, { "url": "http://your-server:9500/static/courses/course01/chapter01/resources/002.mp3", "md5": "...", "title": "第一章 课程002", "target_path": "courses/course01/chapter01/resources/002.mp3", "cover_url": "http://your-server:9500/static/courses/course01/chapter01/resources/002.png", "cover_target_path": "courses/course01/chapter01/resources/002.png" } ] } } ``` **assets 字段说明:** | 字段 | 说明 | |------|------| | url | 资源完整 HTTP 下载地址(服务端自动补全) | | md5 | 文件校验值 | | title | 资源标题 | | target_path | 终端存储相对路径(与服务端目录结构一致) | | cover_url | 封面完整 HTTP 下载地址 | | cover_target_path | 封面终端存储相对路径 | **终端处理逻辑:** 1. 收到 `SYNC_COURSE_TASK` 后解析 `assets` 列表 2. 按每项的 `url` 下载文件,保存到存储卡 `target_path` 路径 3. 按 `cover_url` 下载封面,保存到 `cover_target_path` 4. 下载过程中通过 `rpt/progress` 上报进度 5. 完成后通过 `rpt/ack` 回传 `req_id` 与执行结果 **PRD 预留 Action(可通过 HTTP `/devices/{id}/task` 手动下发):** | Action | payload 示例 | 说明 | |--------|-------------|------| | DOWNLOAD | `{"task_id":"uuid","url":"...","md5":"...","target_path":"..."}` | 单文件下载 | | UPDATE_MANIFEST | `{"course_id":"...","assets":[...]}` | 更新资源清单 | | OTA_UPDATE | `{"url":"...","version":"1.0.2"}` | 固件升级 | | DELETE_FILE | `{"path":"..."}` | 删除文件 | --- ### 3.5 服务端自动下发流程 设备首次上报 `rpt/state` 时,服务端自动执行: ``` 1. cmd/ctl SET_CLOCK {"ts": <当前时间戳>} 2. cmd/ctl SET_VOLUME {"level": 50} 3. cmd/ctl SET_POWER {"state": "wake"} 4. cmd/task SYNC_COURSE_TASK {course01 完整 payload} 5. cmd/task SYNC_COURSE_TASK {course02 完整 payload} ... index.json 中每门基础课程各 1 条 ``` **手动 MQTT 发布示例(mosquitto_pub):** ```bash mosquitto_pub \ -h your-mqtt-broker -p 2100 \ -u your-username -P your-password \ -t "snlp/v1/SN-88293021/cmd/task" \ -q 1 \ -m '{"header":{"version":"1.0","device_id":"SN-88293021","req_id":"manual-uuid","timestamp":1718365200,"ttl":7200},"action":"SYNC_COURSE_TASK","payload":{"version":"1.0","course_id":"course01","course_name":"小智学习课程01","assets":[]}}' ``` --- ## 四、接口速查表 ### HTTP 接口 | 方法 | 路径 | 说明 | |------|------|------| | GET | `/health` | 健康检查 | | GET | `/static/{path}` | 静态资源下载 | | POST | `/api/v1/scene/analyze` | 场景识别 | | GET | `/api/v1/dialogue/stream` | SSE 对话流 | | GET | `/api/v1/courses` | 课程索引 | | GET | `/api/v1/courses/base-manifest` | 全部基础课程同步包预览 | | GET | `/api/v1/courses/{course_id}` | 课程详情 | | GET | `/api/v1/courses/{course_id}/manifest` | 单课程同步包预览 | | GET | `/api/v1/courses/{course_id}/tasks` | 原始 courseTask.json | | GET | `/api/v1/tasklist` | 设备初始化任务清单 | | POST | `/api/v1/devices/init` | 手动设备初始化 | | POST | `/api/v1/devices/course-init` | 手动单课程同步 | | GET | `/api/v1/devices` | 在线设备列表 | | GET | `/api/v1/devices/initialized` | 已初始化设备 | | DELETE | `/api/v1/devices/initialized/{device_id}` | 清除初始化记录 | | GET | `/api/v1/devices/{device_id}` | 设备状态 | | GET | `/api/v1/devices/{device_id}/init-status` | 初始化状态 | | GET | `/api/v1/devices/{device_id}/acks` | 指令回执历史 | | GET | `/api/v1/devices/{device_id}/events` | 事件/进度历史 | | POST | `/api/v1/devices/{device_id}/cmd` | 手动下发 ctl 指令 | | POST | `/api/v1/devices/{device_id}/task` | 手动下发 task 指令 | ### MQTT 接口 | 方向 | Topic | 关键 Action / 用途 | |------|-------|-------------------| | 终端 → 云 | `snlp/v1/{id}/rpt/state` | 心跳,触发自动初始化 | | 终端 → 云 | `snlp/v1/{id}/rpt/event` | ASR 结果、唤醒/休眠事件 | | 终端 → 云 | `snlp/v1/{id}/rpt/ack` | 指令执行回执 | | 终端 → 云 | `snlp/v1/{id}/rpt/progress` | 下载/OTA 进度 | | 终端 → 云 | `snlp/v1/{id}/rpt/log` | 远程排障日志 | | 云 → 终端 | `snlp/v1/{id}/cmd/ctl` | SET_CLOCK / SET_VOLUME / SET_POWER / PLAY_MEDIA 等 | | 云 → 终端 | `snlp/v1/{id}/cmd/task` | SYNC_COURSE_TASK(课程资源包同步) | --- *文档版本:1.0 | 对应服务端版本:1.0.0*