2026-08-17 21:25:46 +08:00
|
|
|
|
# OPC 智能园区后端(FastAPI + MQTT)
|
|
|
|
|
|
|
2026-08-24 11:27:55 +08:00
|
|
|
|
> 📍 工作区位置:`code/dpm/backend/`(DPM 园区大屏后端,独立仓库)。权威说明以本文档为准;系统/运维文档见 `design/dpm/`,前端见 `code/dpm/src/`。
|
|
|
|
|
|
|
2026-08-17 21:25:46 +08:00
|
|
|
|
独立后端服务:**所有数据通过 REST API 提供**,**页面/媒体控制通过 MQTT 下发**,媒体资源由后端统一存储与返回。
|
|
|
|
|
|
|
|
|
|
|
|
## 架构
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
┌──────────────┐ REST(全部数据/媒体/设置) ┌────────────────┐
|
|
|
|
|
|
│ Tauri 大屏 │ ◄────────────────────────── │ FastAPI :10085 │
|
|
|
|
|
|
│ (React 前端) │ └───────┬────────┘
|
|
|
|
|
|
│ │ MQTT 订阅 opc/display/command │ MQTT 发布
|
|
|
|
|
|
│ 账号: dpm │ ◄────────────────────────────────────│ 账号: dpmserver
|
|
|
|
|
|
└──────┬───────┘ MQTT Broker (EMQX) │
|
|
|
|
|
|
│ 192.168.1.3:1883 │
|
|
|
|
|
|
└───────────────────────────────────────────────┘
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
- **数据**:`GET /api/dashboard/snapshot`(每 2.2s 刷新,模拟引擎在后端运行)等
|
|
|
|
|
|
- **控制**:`POST /api/display/command` → MQTT `opc/display/command` → 所有大屏订阅同步响应
|
|
|
|
|
|
- **媒体**:上传/列表/播放列表/`/file` 静态返回(图片/视频统一由后端托管)
|
|
|
|
|
|
- **AI**:通义千问(DashScope)+ 工具调用(切页/控制/卡片/通知,经 MQTT 广播);语音识别用阿里云 paraformer
|
|
|
|
|
|
|
|
|
|
|
|
## 启动
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
cd backend
|
2026-08-17 21:30:44 +08:00
|
|
|
|
uv sync # 安装依赖(fastapi/uvicorn/paho-mqtt/dashscope/jinja2 等)
|
|
|
|
|
|
cp .env.example .env # 按需修改(含 DashScope Key、MQTT 账号)
|
|
|
|
|
|
uv run python main.py # 或 .venv/bin/python main.py
|
2026-08-17 21:25:46 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
启动后:API 与前端页面均在 `http://0.0.0.0:10085`(浏览器直接打开即大屏)。
|
|
|
|
|
|
|
|
|
|
|
|
## 环境变量(backend/.env)
|
|
|
|
|
|
|
|
|
|
|
|
| 变量 | 说明 |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| `DPM_HOST` / `DPM_PORT`(或 `FASTAPI_HOST`/`FASTAPI_PORT`) | 监听地址,默认 `0.0.0.0:10085` |
|
|
|
|
|
|
| `DPM_MQTT_HOST` / `DPM_MQTT_PORT` | MQTT broker 地址,默认读 `MQTT_BROKER_HOST/PORT` |
|
|
|
|
|
|
| `MQTT_USERNAME` / `MQTT_PASSWORD` | **服务端发布账号:dpmserver / 123456** |
|
|
|
|
|
|
| `DASHSCOPE_API_KEY` | 阿里云 DashScope(LLM + 语音识别) |
|
|
|
|
|
|
| `DPM_LLM_MODEL` | 默认 `qwen-plus` |
|
|
|
|
|
|
| `DPM_ASR_MODEL` | 默认 `paraformer-realtime-v2` |
|
|
|
|
|
|
|
|
|
|
|
|
> 前端连接配置在项目根 `.env.local`(已 gitignore):`VITE_API_BASE`、`VITE_MQTT_URL`、`VITE_MQTT_USERNAME=dpm`、`VITE_MQTT_PASSWORD=123456`。
|
|
|
|
|
|
|
|
|
|
|
|
## MQTT 主题
|
|
|
|
|
|
|
|
|
|
|
|
| 主题 | 方向 | 说明 |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| `opc/display/command` | 后端→前端 | `{cmd_id, ts, action, params}`,action: `navigate` / `navigate_rel` / `play` / `pause` / `next` / `prev` / `alert` / `show_card` / `set_mode` |
|
|
|
|
|
|
| `opc/dashboard/tick` | 后端→前端 | 数据快照(`{ts, snapshot}`) |
|
|
|
|
|
|
| `opc/display/ack` | 前端→后端 | 指令回执(预留) |
|
|
|
|
|
|
|
|
|
|
|
|
## 控制示例
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# 切换页面(数据大屏 / 数字孪生 / AI 助手 / 媒体轮播)
|
|
|
|
|
|
curl -X POST http://localhost:10085/api/display/command \
|
|
|
|
|
|
-H "Content-Type: application/json" \
|
|
|
|
|
|
-d '{"action":"navigate","params":{"page":"/twin"}}'
|
|
|
|
|
|
|
|
|
|
|
|
# 媒体控制
|
|
|
|
|
|
curl -X POST http://localhost:10085/api/display/command \
|
|
|
|
|
|
-d '{"action":"next","params":{}}'
|
|
|
|
|
|
|
|
|
|
|
|
# 大屏通知
|
|
|
|
|
|
curl -X POST http://localhost:10085/api/display/command \
|
|
|
|
|
|
-d '{"action":"alert","params":{"title":"园区通知","content":"..."}}'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 关键接口
|
|
|
|
|
|
|
|
|
|
|
|
- `GET /api/dashboard/snapshot` — 大屏全量数据快照
|
|
|
|
|
|
- `GET /api/park/companies` · `GET /api/park/zones` — 企业分布
|
|
|
|
|
|
- `POST /api/ai/chat` — AI 对话(`{messages:[{role,content}]}` → `{reply, tools, mqtt_published}`)
|
|
|
|
|
|
- `POST /api/ai/asr` — 语音识别(multipart 上传录音 + `format` 参数)
|
|
|
|
|
|
- `GET/POST /api/playlist` · `POST /upload` · `GET /media` — 媒体与播放列表
|
|
|
|
|
|
- `POST /api/display/command` — 管理端控制指令(转 MQTT 广播)
|
|
|
|
|
|
- `GET /api/events` — SSE 兼容通道(MQTT 不可用时的前端回退)
|
|
|
|
|
|
|
|
|
|
|
|
## 说明
|
|
|
|
|
|
|
|
|
|
|
|
- 大屏数据由后端模拟引擎产生(`sim_engine.py`,与原前端 `parkData.js` 逻辑一致);前端离线时也有本地兜底
|
|
|
|
|
|
- 媒体文件存放于 `backend/media/`,`/file/...` 直接返回
|
|
|
|
|
|
- 安全:`backend/.env` 与根 `.env.local` 已加入 `.gitignore`,含阿里云密钥与 MQTT 凭据,勿提交
|
2026-08-18 07:14:13 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Docker 部署(推荐)
|
|
|
|
|
|
|
|
|
|
|
|
一键构建并启动 **后端 + EMQX Broker**:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# 1) (可选)配置部署参数
|
|
|
|
|
|
export MQTT_PUBLIC_HOST=192.168.1.50 # 展播端可访问的服务器局域网 IP(Broker WebSocket)
|
|
|
|
|
|
export EMQX_USER=dpmserver
|
|
|
|
|
|
export EMQX_PASS=你的密码
|
|
|
|
|
|
export DASHSCOPE_API_KEY=sk-xxx # AI/语音密钥(未配置时 AI 走本地规则引擎)
|
|
|
|
|
|
export DPM_S2S_ENABLED=1 # 实时语音对话开关
|
|
|
|
|
|
|
|
|
|
|
|
# 2) 构建并启动
|
|
|
|
|
|
docker compose up -d --build
|
|
|
|
|
|
|
|
|
|
|
|
# 3) 验证
|
2026-08-18 09:05:23 +08:00
|
|
|
|
curl http://192.168.1.9:10085/api/health # {"ok":true,...}
|
2026-08-18 07:14:13 +08:00
|
|
|
|
docker compose logs -f backend
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
- 端口:`10085`(API/后台/媒体)、`8765`(s2s 语音)、`1883/8083/18083`(EMQX)
|
|
|
|
|
|
- 数据卷:`dpm_media`(媒体)、`dpm_knowledge`(知识库 park.md,可热更新)、`dpm_data`(设置/播放列表)
|
|
|
|
|
|
- 展播端(Tauri 大屏)启动时经 `GET /api/config` 自动获取 MQTT WebSocket 地址
|
|
|
|
|
|
- 常用命令:`docker compose down` 停止;`docker compose up -d --build backend` 仅重建后端;
|
|
|
|
|
|
`docker compose ps` 查看状态
|
|
|
|
|
|
|
|
|
|
|
|
> 单独构建镜像:`docker build -f backend/Dockerfile -t dpm-backend .`
|