Add batch management APIs, API security, rate limiting, and optimizations
- Batch device CRUD: POST /api/devices/batch (create 500), PUT /api/devices/batch (update 500), POST /api/devices/batch-delete (delete 100) with WHERE IN bulk queries - Batch command: POST /api/commands/batch with model_validator mutual exclusion - API key auth (X-API-Key header, secrets.compare_digest timing-safe) - Rate limiting via SlowAPIMiddleware (60/min default, 30/min writes) - Real client IP extraction (X-Forwarded-For / CF-Connecting-IP) - Global exception handler (no stack trace leaks, passes HTTPException through) - CORS with auto-disable credentials on wildcard origins - Schema validation: IMEI pattern, lat/lon ranges, Literal enums, MAC/UUID patterns - Heartbeats router, per-ID endpoints for locations/attendance/bluetooth - Input dedup in batch create, result ordering preserved - Baidu reverse geocoding, Gaode map tiles with WGS84→GCJ02 conversion - Device detail panel with feature toggles and command controls - Side panel for location/beacon pages with auto-select active device via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run>
This commit is contained in:
100
CLAUDE.md
100
CLAUDE.md
@@ -10,6 +10,7 @@ KKS P240/P241 蓝牙工牌管理后台,基于 FastAPI + SQLAlchemy + asyncio T
|
||||
```
|
||||
/home/gpsystem/
|
||||
├── run.py # 启动脚本 (uvicorn)
|
||||
├── .env.example # 环境变量模板 (复制为 .env 使用)
|
||||
├── requirements.txt # Python 依赖
|
||||
├── frpc.toml # FRP 客户端配置 (TCP隧道)
|
||||
├── badge_admin.db # SQLite 数据库
|
||||
@@ -17,8 +18,10 @@ KKS P240/P241 蓝牙工牌管理后台,基于 FastAPI + SQLAlchemy + asyncio T
|
||||
│
|
||||
├── app/
|
||||
│ ├── main.py # FastAPI 应用入口, 挂载静态文件, 启动TCP服务器
|
||||
│ ├── config.py # 配置 (端口8088 HTTP, 5000 TCP)
|
||||
│ ├── config.py # 配置 (pydantic-settings, .env支持, 端口/API密钥/缓存/限流)
|
||||
│ ├── database.py # SQLAlchemy async 数据库连接
|
||||
│ ├── dependencies.py # FastAPI 依赖 (API Key 认证)
|
||||
│ ├── extensions.py # 共享实例 (rate limiter, 真实IP提取)
|
||||
│ ├── models.py # ORM 模型 (Device, LocationRecord, AlarmRecord, HeartbeatRecord, AttendanceRecord, BluetoothRecord, BeaconConfig, CommandLog)
|
||||
│ ├── schemas.py # Pydantic 请求/响应模型
|
||||
│ ├── tcp_server.py # TCP 服务器核心 (~2400行), 管理设备连接、协议处理、数据持久化
|
||||
@@ -31,18 +34,20 @@ KKS P240/P241 蓝牙工牌管理后台,基于 FastAPI + SQLAlchemy + asyncio T
|
||||
│ │ └── crc.py # CRC-ITU (CRC-16/X-25, 多项式 0x8408)
|
||||
│ │
|
||||
│ ├── services/
|
||||
│ │ ├── device_service.py # 设备 CRUD
|
||||
│ │ ├── command_service.py # 指令日志 CRUD
|
||||
│ │ ├── location_service.py # 位置记录查询
|
||||
│ │ └── beacon_service.py # 蓝牙信标 CRUD
|
||||
│ │ ├── device_service.py # 设备 CRUD
|
||||
│ │ ├── command_service.py # 指令日志 CRUD
|
||||
│ │ ├── location_service.py # 位置记录查询
|
||||
│ │ ├── beacon_service.py # 蓝牙信标 CRUD
|
||||
│ │ └── tcp_command_service.py # TCP指令抽象层 (解耦routers↔tcp_server)
|
||||
│ │
|
||||
│ ├── routers/
|
||||
│ │ ├── devices.py # /api/devices (含 /stats 统计接口)
|
||||
│ │ ├── commands.py # /api/commands (含 /send, /message, /tts)
|
||||
│ │ ├── locations.py # /api/locations (含 /latest, /track)
|
||||
│ │ ├── devices.py # /api/devices (含 /stats, /batch, /batch-delete)
|
||||
│ │ ├── commands.py # /api/commands (含 /send, /message, /tts, /batch)
|
||||
│ │ ├── locations.py # /api/locations (含 /latest, /track, /{id})
|
||||
│ │ ├── alarms.py # /api/alarms (含 acknowledge)
|
||||
│ │ ├── attendance.py # /api/attendance
|
||||
│ │ ├── bluetooth.py # /api/bluetooth
|
||||
│ │ ├── attendance.py # /api/attendance (含 /stats, /{id})
|
||||
│ │ ├── bluetooth.py # /api/bluetooth (含 /{id})
|
||||
│ │ ├── heartbeats.py # /api/heartbeats (心跳记录查询)
|
||||
│ │ └── beacons.py # /api/beacons (信标管理 CRUD)
|
||||
│ │
|
||||
│ └── static/
|
||||
@@ -75,11 +80,18 @@ KKS P240/P241 蓝牙工牌管理后台,基于 FastAPI + SQLAlchemy + asyncio T
|
||||
- **7000**: FRP 服务端管理端口
|
||||
- **7500**: FRP Dashboard (admin/PassWord0325)
|
||||
|
||||
### 配置管理
|
||||
- `app/config.py` 使用 **pydantic-settings** (`BaseSettings`),支持 `.env` 文件覆盖默认值
|
||||
- `.env.example` 提供所有可配置项模板,复制为 `.env` 使用
|
||||
- DATABASE_URL 使用绝对路径 (基于 `__file__` 计算项目根目录)
|
||||
- 所有 API 密钥 (天地图/Google/Unwired/高德) 集中在 `config.py`,`geocoding.py` 从 `settings` 导入
|
||||
- 端口号有 pydantic 校验 (ge=1, le=65535)
|
||||
|
||||
## 启动服务
|
||||
|
||||
```bash
|
||||
# 启动服务
|
||||
cd /tmp/badge-admin
|
||||
cd /home/gpsystem
|
||||
nohup python3 -m uvicorn app.main:app --host 0.0.0.0 --port 8088 > server.log 2>&1 &
|
||||
|
||||
# 启动 FRP 客户端 (TCP隧道)
|
||||
@@ -192,6 +204,19 @@ KKS 二进制协议,详见 `doc/KKS_Protocol_P240_P241.md`
|
||||
- **配额**: 免费 10,000 次/天
|
||||
- **注意**: postStr 参数需使用双引号JSON并URL编码
|
||||
|
||||
### API 认证与限流
|
||||
- **认证**: 设置 `API_KEY` 环境变量后,所有 `/api/` 请求需携带 `X-API-Key` 请求头
|
||||
- **限流**: 全局 60/min (default_limits),写操作 30/min (`@limiter.limit`)
|
||||
- **真实 IP**: 从 `X-Forwarded-For` → `CF-Connecting-IP` → `request.client.host` 提取
|
||||
- **CORS**: `CORS_ORIGINS=*` 时自动禁用 `allow_credentials`
|
||||
|
||||
### 批量操作 API
|
||||
- `POST /api/devices/batch` — 批量创建 (最多500),输入去重 + DB去重,结果按输入顺序
|
||||
- `PUT /api/devices/batch` — 批量更新,单次 WHERE IN 查询 + 单次 flush
|
||||
- `POST /api/devices/batch-delete` — 批量删除 (最多100),通过 body 传 device_ids
|
||||
- `POST /api/commands/batch` — 批量发送指令 (最多100),`model_validator` 互斥校验
|
||||
- 所有批量操作使用 WHERE IN 批量查询,避免 N+1
|
||||
|
||||
### API 分页
|
||||
- page_size 最大限制为 100 (schema 层校验)
|
||||
- 前端设备选择器使用 page_size=100 (不能超过限制)
|
||||
@@ -209,6 +234,7 @@ KKS 二进制协议,详见 `doc/KKS_Protocol_P240_P241.md`
|
||||
- TTS: 通过 0x80 发送 `TTS,<文本>` 格式
|
||||
- 常用指令: `GPSON#` 开启GPS, `BTON#` 开启蓝牙, `BTSCAN,1#` 开启BLE扫描
|
||||
- 已验证可用指令: `BTON#`, `BTSCAN,1#`, `GPSON#`, `MODE,1/3#`, `PARAM#`, `CHECK#`, `VERSION#`, `TIMER#`, `WHERE#`, `STATUS#`, `RESET#`
|
||||
- **架构**: `tcp_command_service.py` 作为抽象层解耦 routers↔tcp_server 的循环导入,通过 lazy import 访问 tcp_manager
|
||||
- **重要**: TCP 层 (`send_command`/`send_message`) 只负责发送,不创建 CommandLog。CommandLog 由 API 层 (commands.py) 管理
|
||||
- **重要**: 服务器启动时自动将所有设备标记为 offline,等待设备重新登录
|
||||
|
||||
@@ -219,7 +245,9 @@ KKS 二进制协议,详见 `doc/KKS_Protocol_P240_P241.md`
|
||||
- **多信标定位**: 0xB3 多信标场景,取 RSSI 信号最强的已注册信标位置
|
||||
- **设备端配置**: 需通过 0x80 指令发送 `BTON#` 开启蓝牙、`BTSCAN,1#` 开启扫描
|
||||
- **已知有效指令**: `BTON#`(btON:1), `BTSCAN,1#`(btSCAN:1) — 设备确认设置成功
|
||||
- **当前状态**: 设备接受BT指令但尚未上报0xB2/0xB3数据,可能需要信标UUID匹配或通过Tracksolid平台配置
|
||||
- **当前状态**: ✅ 0xB2 蓝牙打卡已验证可用 (2026-03-18),设备成功检测信标并上报数据
|
||||
- **已验证信标**: MAC=`C3:00:00:34:43:5E`, UUID=FDA50693-A4E2-4FB1-AFCF-C6EB07647825, Major=10001, Minor=19641
|
||||
- **注意**: 信标需配置经纬度/地址,否则打卡记录中位置为空
|
||||
- **制造商**: 几米物联 (Jimi IoT / jimiiot.com.cn),P240/P241 智能电子工牌系列
|
||||
|
||||
### 0x94 General Info 子协议
|
||||
@@ -289,6 +317,18 @@ remotePort = 5001
|
||||
3. 注意返回坐标为 GCJ-02,需转换为 WGS-84 用于 Leaflet 地图
|
||||
4. 高德数字签名: 参数按key排序拼接 + 安全密钥 → MD5 → sig 参数
|
||||
|
||||
## 百度地图 API
|
||||
|
||||
### Key
|
||||
- **服务端 AK**: `nZ4AyCm7FTn85HbFuQjw0ItSYkgxEuhA`
|
||||
|
||||
### 已接入服务
|
||||
- **✅ 逆地理编码**: `api.map.baidu.com/reverse_geocoding/v3` — 经纬度 → 地址 (coordtype=wgs84ll, 无需坐标转换)
|
||||
- 优先级: 百度 > 天地图 (fallback)
|
||||
- 配额: 5,000次/日 (个人开发者)
|
||||
- **注意**: 百度内部使用 BD-09 坐标系,但逆地理编码接口支持 `coordtype=wgs84ll` 直接传入 WGS-84 坐标
|
||||
- 百度**无服务端基站/WiFi定位API**,基站定位仍用 Mylnikov
|
||||
|
||||
## 已知限制
|
||||
|
||||
1. **IoT SIM 卡不支持 SMS** - 144 号段的物联网卡无法收发短信,需通过平台或 TCP 连接配置设备
|
||||
@@ -296,7 +336,7 @@ remotePort = 5001
|
||||
3. **SQLite 单写** - 高并发场景需切换 PostgreSQL
|
||||
4. **设备最多 100 台列表** - 受 page_size 限制,超过需翻页查询
|
||||
5. **基站定位精度差** - 当前 Mylnikov API 中国基站精度 ~16km,待接入高德智能硬件定位后可达 ~30m
|
||||
6. **天地图逆地理编码使用 HTTP** - API不支持HTTPS,Key在URL中明文传输 (低风险: 免费Key)
|
||||
6. **天地图逆地理编码使用 HTTP** - API不支持HTTPS,Key在URL中明文传输 (低风险: 免费Key, 已降级为备选)
|
||||
|
||||
## 已修复的问题 (Bug Fix 记录)
|
||||
|
||||
@@ -361,6 +401,36 @@ remotePort = 5001
|
||||
42. **前端4G筛选** - 位置类型筛选添加 gps_4g/wifi_4g/lbs_4g 选项
|
||||
43. **DATA_REPORT_MODES** - 修正所有模式名称匹配协议文档
|
||||
|
||||
### 架构优化 (2026-03-17)
|
||||
44. **配置集中化** - API密钥从 geocoding.py 硬编码移至 config.py (pydantic-settings),支持 .env 覆盖
|
||||
45. **数据库绝对路径** - DATABASE_URL 从相对路径改为基于 `__file__` 的绝对路径,避免 CWD 依赖
|
||||
46. **tcp_command_service 抽象层** - 新建 services/tcp_command_service.py,通过 lazy import 解耦 routers↔tcp_server 循环依赖
|
||||
47. **commands.py 去重** - send_command/send_message/send_tts 提取 `_send_to_device()` 通用函数
|
||||
48. **协议常量扩展** - constants.py 新增 DEFAULT_DEVICE_TYPE, SERVER_FLAG_BYTES, ATTENDANCE_TYPES, COURSE_BIT_*, MCC_MNC2_FLAG, VOLTAGE_LEVELS
|
||||
49. **前端侧边面板** - 位置追踪/信标管理页面添加左侧设备/信标列表面板,自动选中最近活跃设备
|
||||
50. **前端面板解耦** - 提取 PANEL_IDS 配置 + _initPanelRender 通用函数,toggleSidePanel 仅在 locations 页调用 invalidateSize
|
||||
|
||||
### 百度地图接入 & 连接修复 (2026-03-19)
|
||||
51. **百度逆地理编码** - 接入百度地图 reverse_geocoding/v3 API (coordtype=wgs84ll),优先于天地图
|
||||
52. **PROTO_ADDRESS_REPLY_EN 未导入** - 0xA5 报警地址回复时 NameError,补充 import
|
||||
53. **心跳自动恢复 online** - 心跳处理时自动将设备 status 设为 online + 重新注册 connections
|
||||
54. **高德地图瓦片** - 替换天地图瓦片为高德 (GCJ-02, 标准Mercator),添加 WGS84→GCJ02 坐标转换,可切换 provider (`MAP_PROVIDER` 变量)
|
||||
|
||||
### API 安全加固 & 批量管理 (2026-03-20)
|
||||
55. **API Key 认证** - `dependencies.py` 实现 X-API-Key 头认证,`secrets.compare_digest` 防时序攻击
|
||||
56. **CORS + 限流** - `SlowAPIMiddleware` 全局限流 (60/min),写操作独立限速 (30/min)
|
||||
57. **限流器真实 IP** - `extensions.py` 从 `X-Forwarded-For` / `CF-Connecting-IP` 提取真实客户端 IP
|
||||
58. **全局异常处理** - 拦截未处理异常返回 500,不泄露堆栈;放行 HTTPException/ValidationError
|
||||
59. **Schema 校验加强** - IMEI pattern、经纬度范围、Literal 枚举、command max_length、BeaconConfig MAC/UUID pattern
|
||||
60. **Health 端点增强** - `/health` 检测数据库连通性 + TCP 连接设备数
|
||||
61. **批量设备创建** - `POST /api/devices/batch` (最多500台),WHERE IN 单次查询去重,输入列表内 IMEI 去重
|
||||
62. **批量设备更新** - `PUT /api/devices/batch`,单次查询 + 批量更新 + 单次 flush
|
||||
63. **批量设备删除** - `POST /api/devices/batch-delete`,通过 body 传递避免 URL 长度限制
|
||||
64. **批量指令发送** - `POST /api/commands/batch` (最多100台),`model_validator` 互斥校验 device_ids/imeis
|
||||
65. **Heartbeats 路由** - 新增 `GET /api/heartbeats` 心跳记录查询 + 按 ID 获取
|
||||
66. **按 ID 查询端点** - locations/{id}, attendance/{id}, bluetooth/{id} 放在路由末尾避免冲突
|
||||
67. **Beacons double-commit 修复** - 移除 router 层多余的 flush/refresh,依赖 service 层
|
||||
|
||||
### 0x94 子协议 0x04
|
||||
- 设备配置上报: `ALM2=40;ALM4=E0;MODE=03;IMSI=460240388355286`
|
||||
- 在设备重连/重启后上报
|
||||
@@ -368,9 +438,9 @@ remotePort = 5001
|
||||
## 待完成功能
|
||||
|
||||
1. **⭐ 接入高德智能硬件定位** - 企业认证通过后,替换 Mylnikov,大幅提升 WiFi/基站定位精度
|
||||
2. **天地图瓦片底图** - 使用浏览器端 Key 替换 OpenStreetMap 瓦片 (中国地区显示更准确)
|
||||
2. ~~**地图瓦片**~~ - ✅ 已切换为高德瓦片 (GCJ-02),支持 MAP_PROVIDER 切换 ('gaode'|'tianditu')
|
||||
3. **心跳扩展模块解析** - 计步器、外部电压等模块未解析
|
||||
4. **蓝牙信标调试** - P241 接受 BTON/BTSCAN 但未上报BLE数据,需确认信标iBeacon广播格式及UUID匹配
|
||||
4. ~~**蓝牙信标调试**~~ - ✅ 已完成 (2026-03-18),0xB2打卡数据正常上报,信标匹配成功
|
||||
|
||||
## 调试技巧
|
||||
|
||||
|
||||
Reference in New Issue
Block a user