Files
desungongpai/CLAUDE.md
default d54e53e0b7 feat: KKS P240/P241 蓝牙工牌管理系统初始提交
FastAPI + SQLAlchemy + asyncio TCP 服务器,支持设备管理、实时定位、
告警、考勤打卡、蓝牙记录、指令下发、TTS语音播报等功能。
2026-03-27 10:19:34 +00:00

30 KiB
Raw Blame History

Badge Admin - KKS P240/P241 蓝牙工牌管理系统

项目概览

KKS P240/P241 蓝牙工牌管理后台,基于 FastAPI + SQLAlchemy + asyncio TCP 服务器。 支持设备管理、实时定位、告警、考勤打卡、蓝牙记录、指令下发、TTS语音播报等功能。

项目结构

/home/gpsystem/
├── run.py                      # 启动脚本 (uvicorn)
├── .env.example                # 环境变量模板 (复制为 .env 使用)
├── requirements.txt            # Python 依赖
├── frpc.toml                   # FRP 客户端配置 (TCP隧道)
├── badge_admin.db              # SQLite 数据库
├── server.log                  # 服务器日志
│
├── app/
│   ├── main.py                 # FastAPI 应用入口, 挂载静态文件, 启动TCP服务器
│   ├── config.py               # 配置 (pydantic-settings, .env支持, 端口/API密钥/缓存/限流)
│   ├── database.py             # SQLAlchemy async 数据库连接
│   ├── dependencies.py         # FastAPI 依赖 (多API Key认证 + 权限控制: read/write/admin)
│   ├── extensions.py           # 共享实例 (rate limiter, 真实IP提取)
│   ├── websocket_manager.py    # WebSocket 连接管理器 (topic订阅, 实时广播)
│   ├── models.py               # ORM 模型 (Device, LocationRecord, AlarmRecord, HeartbeatRecord, AttendanceRecord, BluetoothRecord, BeaconConfig, CommandLog, ApiKey)
│   ├── schemas.py              # Pydantic 请求/响应模型
│   ├── tcp_server.py           # TCP 服务器核心 (~2400行), 管理设备连接、协议处理、数据持久化
│   ├── geocoding.py            # 高德地理编码服务 (基站/WiFi → 经纬度 + 经纬度 → 地址)
│   │
│   ├── protocol/
│   │   ├── constants.py        # 协议常量 (协议号、告警类型snake_case、信号等级等)
│   │   ├── parser.py           # 二进制协议解析器
│   │   ├── builder.py          # 响应包构建器
│   │   └── 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
│   │   └── tcp_command_service.py  # TCP指令抽象层 (解耦routers↔tcp_server)
│   │
│   ├── routers/
│   │   ├── devices.py          # /api/devices (含 /stats, /batch, /batch-delete, /all-latest-locations)
│   │   ├── commands.py         # /api/commands (含 /send, /message, /tts, /batch)
│   │   ├── locations.py        # /api/locations (含 /latest, /batch-latest, /track, /{id})
│   │   ├── alarms.py           # /api/alarms (含 acknowledge, alarm_source过滤)
│   │   ├── attendance.py       # /api/attendance (含 /stats, /{id})
│   │   ├── bluetooth.py        # /api/bluetooth (含 beacon_mac过滤, /{id})
│   │   ├── heartbeats.py       # /api/heartbeats (心跳记录查询)
│   │   ├── beacons.py          # /api/beacons (信标管理 CRUD)
│   │   ├── api_keys.py         # /api/keys (API密钥管理 CRUD, admin only)
│   │   └── ws.py               # /ws (WebSocket实时推送, topic订阅)
│   │
│   └── static/
│       └── admin.html          # 管理后台 SPA (暗色主题, 8个页面)
│
└── doc/
    └── KKS_Protocol_P240_P241.md  # 协议文档 (从PDF转换)

架构设计

网络拓扑

[P241 工牌] --TCP--> [152.69.205.186:5001 (frps)]
                            |
                        FRP Tunnel
                            |
                      [Container:5000 (frpc)]
                            |
                      [TCP Server (asyncio)]
                            |
                      [FastAPI :8088] --CF Tunnel--> [Web 访问]

端口说明

  • 8088: FastAPI HTTP API + 管理后台 UI
  • 5000: TCP 服务器 (KKS 二进制协议)
  • 5001: 远程服务器 FRP 映射端口 (152.69.205.186)
  • 7000: FRP 服务端管理端口
  • 7500: FRP Dashboard (admin/PassWord0325)

配置管理

  • app/config.py 使用 pydantic-settings (BaseSettings),支持 .env 文件覆盖默认值
  • .env.example 提供所有可配置项模板,复制为 .env 使用
  • DATABASE_URL 使用绝对路径 (基于 __file__ 计算项目根目录)
  • 高德 API 密钥集中在 config.pygeocoding.pysettings 导入
  • 端口号有 pydantic 校验 (ge=1, le=65535)

启动服务

# 启动服务
cd /home/gpsystem
nohup python3 -m uvicorn app.main:app --host 0.0.0.0 --port 8088 > server.log 2>&1 &

# 启动 FRP 客户端 (TCP隧道)
nohup /tmp/frpc -c /home/gpsystem/frpc.toml > /tmp/frpc.log 2>&1 &

# 检查服务状态
curl http://localhost:8088/health
curl http://localhost:8088/api/devices

管理后台 UI

访问 /admin 路径,包含以下页面:

  1. 仪表盘 - 设备统计、告警概览、在线状态
  2. 设备管理 - 添加/编辑/删除设备
  3. 位置追踪 - Leaflet 地图、轨迹回放、地址显示
  4. 告警管理 - 告警列表、确认处理、位置/地址显示
  5. 考勤记录 - 上下班打卡记录 (0xB0/0xB1 GPS+LBS+WiFi)
  6. 蓝牙记录 - 蓝牙打卡(0xB2)/定位(0xB3)记录含信标MAC/UUID/RSSI等
  7. 信标管理 - 蓝牙信标注册、位置配置MAC/UUID/楼层/区域/经纬度)
  8. 指令管理 - 发送指令/留言/TTS语音

协议详情

KKS 二进制协议,详见 doc/KKS_Protocol_P240_P241.md

已支持协议号

协议号 名称 方向
0x01 Login 登录 终端→服务器
0x13 Heartbeat 心跳 双向
0x1F Time Sync 时间同步 双向
0x22 GPS 定位 终端→服务器
0x26 Alarm ACK 告警确认 服务器→终端
0x28 LBS 多基站 终端→服务器
0x2C WiFi 定位 终端→服务器
0x36 Heartbeat Extended 扩展心跳 双向
0x80 Online Command 在线指令 服务器→终端
0x81 Online Command Reply 终端→服务器
0x82 Message 留言 服务器→终端
0x94 General Info (ICCID/IMSI等) 双向
0xA0 4G GPS 定位 终端→服务器
0xA1 4G LBS 多基站 终端→服务器
0xA2 4G WiFi 定位 终端→服务器
0xA3-0xA5 4G 告警 (围栏/LBS) 终端→服务器
0xA9 WiFi 告警 终端→服务器
0xB0-0xB1 考勤打卡 双向
0xB2 蓝牙打卡 双向
0xB3 蓝牙定位 终端→服务器

数据包格式

[Start 0x7878/0x7979] [Length 1-2B] [Protocol 1B] [Content NB] [Serial 2B] [CRC 2B] [Stop 0x0D0A]

当前设备

字段
IMEI 868120334031363
型号 P241
名称 测试
DB ID 1
ICCID 89860848102570005286
IMSI 460240388355286
SIM卡 中国移动 IoT (MCC=460, MNC=0)
位置 成都地区 (~30.605°N, 103.936°E)

重要实现细节

IMEI 解析

  • BCD 编码: 8字节 = 16 hex digits, 首位为填充 0
  • 使用 raw_hex[1:] 只移除首位填充0 (不用 lstrip 避免移除多个)

设备登录

  • 登录时不覆盖用户设置的 device_type (避免被覆盖为 raw hex)
  • 重连时先关闭旧连接再建立新连接
  • 断开连接时检查 writer 是否匹配,避免误将新连接标记为 offline

告警处理

  • 告警响应使用 0x26 ACK (非原始协议号回复)
  • 告警类型常量使用 snake_case (如 sos, vibration, enter_fence)
  • 4G 告警包解析:
    • 0xA3/0xA4 (围栏告警): datetime(6) + gps(12) + lbs_length(1) + MCC/MNC + LAC(4) + CellID(8) + terminal_info(1) + voltage_level(1字节) + gsm_signal(1) + alarm_code(1) + language(1)
    • 0xA5 (LBS告警): 无datetime, 无GPS — 直接从 MCC 开始
    • 0xA9 (WiFi告警): datetime(6) + MCC/MNC + cell_type(1) + cell_count(1) + 基站列表 + timing_advance(1) + WiFi列表 + alarm_code(1) + language(1)
  • MCC 高位 0x8000 标志: 表示 MNC 为 2 字节 (非默认1字节)
  • voltage_level 是 1字节 (0x00-0x06 等级)不是2字节毫伏值
  • LBS/WiFi 报警自动执行前向地理编码(基站→经纬度)+ 逆地理编码(经纬度→地址)

位置数据与地理编码

  • GPS 定位 (0x22/0xA0): 直接包含经纬度坐标,精度最高 (~10m)
  • LBS 基站定位 (0x28/0xA1): 包含 MCC/MNC/LAC/CellID需要地理编码转换为经纬度
  • WiFi 定位 (0x2C/0xA2): 包含基站数据 + WiFi AP MAC地址列表需要地理编码
  • 所有地理编码服务统一使用高德 (Amap)
  • 前向地理编码 (geocode_location): 基站/WiFi → 经纬度
    • 高德智能硬件定位: apilocate.amap.com/position,需企业认证 (认证中,个人账号返回 10012)
    • WiFi+基站混合定位精度 ~30m企业认证通过后自动生效
  • 逆地理编码 (reverse_geocode): 经纬度 → 中文地址
    • 高德: restapi.amap.com/v3/geocode/regeo,需 WGS84→GCJ02 坐标转换 (服务端 Python 实现)
    • 缓存策略: 坐标四舍五入到3位小数 (~100m) 作为缓存key
  • 内置 LRU 缓存 (maxsize=10000),避免重复请求相同基站/坐标
  • WGS84→GCJ02 服务端转换: geocoding.py 内置 wgs84_to_gcj02() 函数 (与前端 JS 版一致)
  • 高德数字签名: 参数按key排序拼接 + AMAP_SECRET → MD5 → sig 参数

API 认证与限流

  • 认证: 设置 API_KEY 环境变量后,所有 /api/ 请求需携带 X-API-Key 请求头
  • 多 API Key: 支持 master key (环境变量) + 数据库管理的 API Key (SHA-256 hash)
  • 权限级别: read (只读) < write (读写) < admin (管理,含 key 管理)
  • 权限控制: 所有 POST/PUT/DELETE 端点需要 write 权限,/api/keys 需要 admin 权限
  • Key 管理: POST /api/keys 创建 key (返回明文一次), GET /api/keys 列表, PUT /api/keys/{id} 更新, DELETE /api/keys/{id} 停用
  • 限流: 全局 60/min (default_limits),写操作 30/min (@limiter.limit)
  • 真实 IP: 从 X-Forwarded-ForCF-Connecting-IPrequest.client.host 提取
  • CORS: CORS_ORIGINS=* 时自动禁用 allow_credentials

WebSocket 实时推送

  • 端点: ws://host/ws?api_key=xxx&topics=location,alarm
  • Topics: location, alarm, device_status, attendance, bluetooth
  • 认证: query param api_key (支持 master key + DB key)
  • 最大连接: 100 个 WebSocket 连接
  • 消息格式: JSON {"topic": "...", "data": {...}, "timestamp": "..."}
  • 广播点: 位置存储、报警存储、设备上下线、考勤存储、蓝牙存储
  • Ping/Pong: 客户端发送 "ping" 保活,服务器回复 "pong"

数据清理

  • 自动清理: 后台定时任务,每 DATA_CLEANUP_INTERVAL_HOURS(默认24) 小时执行
  • 保留天数: DATA_RETENTION_DAYS(默认90) 天,删除过期的 location/heartbeat/alarm/attendance/bluetooth 记录
  • 手动清理: POST /api/system/cleanup (admin only)

批量操作 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/locations/batch-latest — 批量获取多设备最新位置 (最多100)
  • GET /api/devices/all-latest-locations — 获取所有在线设备最新位置
  • POST /api/commands/batch — 批量发送指令 (最多100)model_validator 互斥校验
  • 所有批量操作使用 WHERE IN 批量查询,避免 N+1

API 分页

  • page_size 最大限制为 100 (schema 层校验)
  • 前端设备选择器使用 page_size=100 (不能超过限制)

CRC 算法

  • CRC-ITU / CRC-16/X-25
  • Reflected polynomial: 0x8408
  • Initial value: 0xFFFF
  • Final XOR: 0xFFFF

TCP 指令下发

  • 0x80 (Online Command): ASCII 指令 + 2字节语言字段 (0x0001=中文)
  • 0x81 (Command Reply): 设备回复,格式 length(1) + server_flag(4) + content(n) + language(2)
  • 0x82 (Message): UTF-16 BE 编码的文本消息 (非UTF-8)
  • 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等待设备重新登录

蓝牙信标管理

  • BeaconConfig 表: 注册蓝牙信标,配置 MAC/UUID/Major/Minor/楼层/区域/经纬度/地址
  • 自动关联: 0xB2 打卡和 0xB3 定位时,根据 beacon_mac 查询 beacon_configs 表
  • 位置写入: 将已注册信标的经纬度写入 BluetoothRecord 的 latitude/longitude
  • 多信标定位: 0xB3 多信标场景,取 RSSI 信号最强的已注册信标位置
  • 设备端配置: 需通过 0x80 指令发送 BTON# 开启蓝牙、BTSCAN,1# 开启扫描
  • 已知有效指令: BTON#(btON:1), BTSCAN,1#(btSCAN:1) — 设备确认设置成功
  • 当前状态: 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 子协议

  • 子协议 0x0A: IMEI(8字节) + IMSI(8字节) + ICCID(10字节)
  • 子协议 0x09: GPS 卫星状态
  • 子协议 0x00: ICCID(10字节)
  • 子协议 0x04: 设备配置上报 ALM2=40;ALM4=E0;MODE=03;IMSI=...

前端字段映射 (admin.html)

  • 设备信号: d.gsm_signal (非 d.signal_strength)
  • 指令响应: c.response_content (非 c.response)
  • 响应时间: c.response_at || c.sent_at (非 c.updated_at)
  • 位置地址: l.address (高德逆地理编码结果)
  • 卫星数: l.gps_satellites (非 l.accuracy)
  • 记录时间: l.recorded_at (非 l.timestamp)
  • 报警来源: a.alarm_source (非 a.source)
  • 报警信号: a.gsm_signal (非 a.signal_strength)
  • 报警类型: snake_case (如 vibration, power_cut, voice_alarm)

FRP 配置

# frpc.toml (容器端)
serverAddr = "152.69.205.186"
serverPort = 7000
auth.method = "token"
auth.token = "PassWord0325"

[[proxies]]
name = "badge-tcp"
type = "tcp"
localIP = "127.0.0.1"
localPort = 5000
remotePort = 5001

外部服务器

  • IP: 152.69.205.186
  • OS: Ubuntu 20.04, Python 3.11, 24GB RAM
  • FRP Server: frps v0.62.1 (1panel Docker, network_mode: host)
  • Config: /opt/1panel/apps/frps/frps/data/frps.toml

高德地图 API

Key

  • Web服务 Key: a9f4e04f5c8e739e5efb07175333f30b
  • 安全密钥: bfc4e002c49ab5f47df71e0aeaa086a5
  • 账号类型: 个人认证开发者 (正在申请企业认证)

已接入服务

  • 逆地理编码 (restapi.amap.com/v3/geocode/regeo): 经纬度 → 地址文本,服务端 WGS84→GCJ02 坐标转换
  • 智能硬件定位 (apilocate.amap.com/position): WiFi+基站 → 经纬度 (代码就绪,企业认证通过前返回 10012)
  • 前端地图瓦片: 高德瓦片 (GCJ-02, 标准Mercator),前端 WGS84→GCJ02 坐标转换
  • 数字签名: _amap_sign() — 参数按key排序拼接 + AMAP_SECRET → MD5 → sig 参数

配额 (个人认证开发者)

  • 基础LBS服务: 5,000 次/日 (逆地理编码等)
  • 在线定位: 50,000 次/日 (企业认证后 1,000,000 次/日)

已知限制

  1. IoT SIM 卡不支持 SMS - 144 号段的物联网卡无法收发短信,需通过平台或 TCP 连接配置设备
  2. Cloudflare Tunnel 仅代理 HTTP - TCP 流量必须通过 FRP 转发
  3. SQLite 单写 - 高并发场景需切换 PostgreSQL
  4. 设备最多 100 台列表 - 受 page_size 限制,超过需翻页查询
  5. 基站/WiFi定位需企业认证 - 高德智能硬件定位 API 已接入但个人账号返回 10012企业认证通过后自动生效

已修复的问题 (Bug Fix 记录)

数据链路修复

  1. IMEI 解析 - 修复 BCD 解码逻辑,确保正确提取 15 位 IMEI
  2. 设备类型覆盖 - 登录时不再覆盖用户设置的 device_type
  3. 设备重连 - 新连接建立前先关闭旧连接,断开时检查 writer 身份
  4. 告警类型提取 - 修正 alarm byte 位置,从 terminal_info+voltage+gsm_signal 之后读取

协议修复

  1. 0x80 指令包 - 添加缺失的 2 字节语言字段 (0x0001)
  2. 0x82 消息编码 - 从 UTF-8 改为 UTF-16 BE (协议要求)
  3. 0x94 通用信息 - 完善子协议 0x0A (IMEI+IMSI+ICCID) 和 0x09 (GPS卫星状态) 解析

前端修复

  1. 设备选择器为空 - page_size=500 超过 API 最大限制 100返回 422 错误
  2. 位置页面崩溃 - API 返回 data: null 时未做空值检查
  3. 轨迹查询失败 - 缺少必填的 start_time/end_time 参数
  4. 字段名不匹配 - 修复 signal_strength→gsm_signal, response→response_content, source→alarm_source 等

定位功能修复

  1. WiFi/LBS 无坐标 - 添加 wifi/wifi_4g 解析分支 (原代码缺失)
  2. 地理编码集成 - LBS/WiFi 定位数据自动转换为经纬度坐标
  3. 邻近基站和WiFi数据 - 存储到 LocationRecord 的 neighbor_cells 和 wifi_data 字段

告警功能修复

  1. 告警响应协议 - 改为使用 0x26 ACK 响应 (原来错误使用地址回复)
  2. voltage_level 解析 - 从2字节改为1字节 (0x00-0x06等级)
  3. 0xA5 LBS告警格式 - 移除错误的 datetime/lbs_length 前缀,直接从 MCC 开始
  4. 0xA9 WiFi告警 - 独立解析器 (非GPS格式),支持 cell_type/cell_count/WiFi AP 列表
  5. MNC 2字节标志 - _parse_mcc_mnc_from_content 正确处理 MCC 高位 0x8000 标志
  6. 告警类型常量 - 改为 snake_case新增 voice_alarm(0x16), fake_base_station(0x17), cover_open(0x18), internal_low_battery(0x19)
  7. LBS/WiFi 报警定位 - 为无GPS的报警添加前向地理编码

逆地理编码

  1. 逆地理编码集成 - 位置和报警记录自动获取中文地址 (高德逆地理编码)
  2. 地址字段 - LocationRecord 新增 address 字段,前端位置表/报警表/地图弹窗显示地址

蓝牙与考勤功能

  1. 0xB0/0xB1 考勤解析 - 完整解析 GPS+LBS+WiFi 考勤包含基站邻区、WiFi AP、前向+逆地理编码
  2. 0xB2 蓝牙打卡解析 - 解析 iBeacon 数据 (RSSI/MAC/UUID/Major/Minor/电池/打卡类型)
  3. 0xB3 蓝牙定位解析 - 解析多信标数据,每信标 30 字节,支持电压(V)/百分比(%)电池单位
  4. 考勤类型检测 - 从 terminal_info 字节 bits[5:2] 判断 clock_in(0001)/clock_out(0010)
  5. 信标管理系统 - BeaconConfig CRUD注册信标物理位置TCP 自动关联信标经纬度

指令发送修复

  1. server_flag_str 未定义 - send_command 中记录日志时变量未定义导致 NameError
  2. TCP 层重复创建日志 - 简化 send_command/send_message 只负责发送CommandLog 由 API 层管理
  3. IMEI lstrip 问题 - lstrip("0") 可能删除多个前导零,改为 raw_hex[1:]
  4. 设备启动状态 - 服务器启动时重置所有设备为 offline避免显示在线但实际未连接
  5. 0x81 回复解析 - 去除末尾 2 字节语言字段,正确提取回复文本

协议全面审计修复 (2026-03-16)

  1. 0x1F 时间同步格式 - 从6字节(YY MM DD HH MM SS)改为4字节Unix时间戳+2字节语言字段
  2. 地址回复完整格式 - 重写 _send_address_reply,实现完整格式: server_flag(4)+ADDRESS/ALARMSMS(7-8)+&&+UTF16BE地址+&&+电话(21)+##
  3. 报警地址回复 - 报警包在发送0x26 ACK后额外发送0x17地址回复(含ALARMSMS标记)
  4. 0x82 留言格式 - 添加 server_flag(4字节) + language(2字节) 字段
  5. 0x22 GPS Cell ID - 从2字节改为3字节解析 (2G基站CellID为3字节)
  6. 登录包时区/语言 - 解析 content[10:12] 提取时区(bit15-04)和语言(bit00)存入Device
  7. 0x94 不需回复 - 移除服务器响应,从 PROTOCOLS_REQUIRING_RESPONSE 中删除
  8. WiFi缓存+LRU淘汰 - 新增 LRUCache(OrderedDict, maxsize=10000)替换所有dict缓存修复WiFi缓存未写入
  9. 前端4G筛选 - 位置类型筛选添加 gps_4g/wifi_4g/lbs_4g 选项
  10. DATA_REPORT_MODES - 修正所有模式名称匹配协议文档

架构优化 (2026-03-17)

  1. 配置集中化 - API密钥从 geocoding.py 硬编码移至 config.py (pydantic-settings),支持 .env 覆盖
  2. 数据库绝对路径 - DATABASE_URL 从相对路径改为基于 __file__ 的绝对路径,避免 CWD 依赖
  3. tcp_command_service 抽象层 - 新建 services/tcp_command_service.py通过 lazy import 解耦 routers↔tcp_server 循环依赖
  4. commands.py 去重 - send_command/send_message/send_tts 提取 _send_to_device() 通用函数
  5. 协议常量扩展 - constants.py 新增 DEFAULT_DEVICE_TYPE, SERVER_FLAG_BYTES, ATTENDANCE_TYPES, COURSE_BIT_*, MCC_MNC2_FLAG, VOLTAGE_LEVELS
  6. 前端侧边面板 - 位置追踪/信标管理页面添加左侧设备/信标列表面板,自动选中最近活跃设备
  7. 前端面板解耦 - 提取 PANEL_IDS 配置 + _initPanelRender 通用函数toggleSidePanel 仅在 locations 页调用 invalidateSize

连接修复 (2026-03-19)

  1. PROTO_ADDRESS_REPLY_EN 未导入 - 0xA5 报警地址回复时 NameError补充 import
  2. 心跳自动恢复 online - 心跳处理时自动将设备 status 设为 online + 重新注册 connections

全面切换高德 API (2026-03-23)

  1. 高德逆地理编码 - 接入 restapi.amap.com/v3/geocode/regeo服务端 WGS84→GCJ02 坐标转换
  2. 高德智能硬件定位 - 接入 apilocate.amap.com/position (基站+WiFi定位企业认证通过前返回 10012)
  3. 高德数字签名 - 实现 _amap_sign() 函数 (参数排序 + AMAP_SECRET + MD5)
  4. 服务端坐标转换 - geocoding.py 内置 wgs84_to_gcj02() Python 实现
  5. 高德地图瓦片 - 前端替换为高德瓦片 (GCJ-02, 标准Mercator),前端 WGS84→GCJ02 坐标转换
  6. 移除第三方地理编码 - 清理天地图/百度/Google/Unwired/Mylnikov统一使用高德

API 安全加固 & 批量管理 (2026-03-20)

  1. API Key 认证 - dependencies.py 实现 X-API-Key 头认证,secrets.compare_digest 防时序攻击
  2. CORS + 限流 - SlowAPIMiddleware 全局限流 (60/min),写操作独立限速 (30/min)
  3. 限流器真实 IP - extensions.pyX-Forwarded-For / CF-Connecting-IP 提取真实客户端 IP
  4. 全局异常处理 - 拦截未处理异常返回 500不泄露堆栈放行 HTTPException/ValidationError
  5. Schema 校验加强 - IMEI pattern、经纬度范围、Literal 枚举、command max_length、BeaconConfig MAC/UUID pattern
  6. Health 端点增强 - /health 检测数据库连通性 + TCP 连接设备数
  7. 批量设备创建 - POST /api/devices/batch (最多500台)WHERE IN 单次查询去重,输入列表内 IMEI 去重
  8. 批量设备更新 - PUT /api/devices/batch,单次查询 + 批量更新 + 单次 flush
  9. 批量设备删除 - POST /api/devices/batch-delete,通过 body 传递避免 URL 长度限制
  10. 批量指令发送 - POST /api/commands/batch (最多100台)model_validator 互斥校验 device_ids/imeis
  11. Heartbeats 路由 - 新增 GET /api/heartbeats 心跳记录查询 + 按 ID 获取
  12. 按 ID 查询端点 - locations/{id}, attendance/{id}, bluetooth/{id} 放在路由末尾避免冲突
  13. Beacons double-commit 修复 - 移除 router 层多余的 flush/refresh依赖 service 层

全面改进 (2026-03-22)

Protocol 修复

  1. parser.py 0x22 CellID - 从2字节改为3字节解析
  2. parser.py 0xA5 LBS告警 - 移除错误的 datetime+lbs_length 前缀
  3. parser.py 0xA9 WiFi告警 - 完全重写为 datetime+MCC/MNC+cell_type+cell_count+基站+TA+WiFi+alarm_code
  4. parser.py voltage - 0xA3/0xA5/0xA9 voltage 从2字节改为1字节
  5. parser.py 0xA4 - 新增多围栏告警解析器 (含 fence_id)
  6. parser.py 0xB2 - 完整 iBeacon 解析 (RSSI/MAC/UUID/Major/Minor/Battery)
  7. parser.py 0xB3 - beacon_count + 每信标30字节解析
  8. parser.py 0xB0/0xB1 - 补充 gps_positioned/terminal_info/voltage/gsm_signal 等中间字段
  9. parser.py 0x81 - 新增指令回复解析器
  10. builder.py 0x1F - 时间同步添加2字节 language 参数
  11. builder.py 地址回复 - 重写 CN/EN 地址回复完整格式
  12. builder.py 0x80/0x82 - cmd_len 正确包含 language(2)
  13. constants.py - 从 PROTOCOLS_REQUIRING_RESPONSE 移除 0x28

批量 API + 索引

  1. batch-latest - POST /api/locations/batch-latest 批量获取多设备最新位置
  2. all-latest-locations - GET /api/devices/all-latest-locations 所有在线设备位置
  3. 数据库索引 - 新增5个索引 (alarm_type, acknowledged, beacon_mac, location_type, attendance_type)

多 API Key + 权限控制

  1. ApiKey 模型 - SHA-256 hash, permissions(read/write/admin), is_active
  2. 多 Key 认证 - master key (env) + DB keys, last_used_at 追踪
  3. 权限控制 - require_write/require_admin 依赖,写端点需 write 权限
  4. Key 管理 API - /api/keys CRUD (admin only),创建时返回明文一次
  5. DeviceUpdate - 移除 status 字段 (设备状态由系统管理)

WebSocket 实时推送

  1. WebSocketManager - 连接管理器topic 订阅最大100连接
  2. WS 端点 - /ws?api_key=xxx&topics=location,alarm WebSocket 认证
  3. TCP 广播集成 - 7个广播点 (location, alarm, device_status x2, attendance, bluetooth x2)

数据清理 + 补充

  1. 自动数据清理 - 后台定时任务DATA_RETENTION_DAYS=90, DATA_CLEANUP_INTERVAL_HOURS=24
  2. 手动清理 API - POST /api/system/cleanup (admin only)
  3. alarm_source 过滤 - alarms 列表新增 alarm_source 参数
  4. beacon_mac 过滤 - bluetooth 列表新增 beacon_mac 参数
  5. command_type 过滤 - commands 列表新增 command_type 参数
  6. sort_order 参数 - alarms/bluetooth 列表支持 asc/desc 排序

协议层统一

  1. PacketBuilder 统一 - tcp_server.py 内嵌 PacketBuilder 改为委托 protocol/builder.py

审计修复 (2026-03-23)

  1. require_admin 导入 - main.py 缺少 require_admin 导入,设置 API_KEY 后 cleanup 端点崩溃
  2. 0x28/0x2C CellID 3字节 - tcp_server.py 2G 基站 CellID 从2字节修正为3字节 (0x28 LBS, 0x2C WiFi, 邻区解析)
  3. parser.py CellID 3字节 - parse_lbs_station 默认 cell_id_size=2→3新增3字节分支; _parse_lbs_address_req CellID 2→3字节
  4. alarm_source 字段长度 - models.py String(10)→String(20), schemas.py max_length=10→20 ("single_fence"=12字符)
  5. AlarmRecord wifi_data/fence_data - 0xA9 WiFi报警存储 wifi_data; 0xA4 多围栏报警提取并存储 fence_id
  6. CommandLog.sent_at - 指令发送成功后设置 sent_at 时间戳 (原来始终为 NULL)
  7. geocoding IMEI 参数化 - 移除硬编码 IMEI新增 GEOCODING_DEFAULT_IMEI 配置项geocode_location 接受 imei 参数TCP 层传递设备 IMEI
  8. parser.py 循环长度检查 - _parse_lbs_multi 和 _parse_wifi 站点循环检查从 pos+5 改为 pos+6 (LAC=2+CellID=3+RSSI=1=6)
  9. batch sent_at - batch_send_command 批量指令路径也设置 cmd_log.sent_at (与单条路径一致)
  10. GPS 经度符号反转 - course_status bit 11 含义为 0=东经/1=西经 (非 1=东经)is_east 改为 is_west,修复成都定位到北美洲的问题 (tcp_server.py + parser.py)

待完成功能

  1. 心跳扩展模块解析 - 计步器、外部电压等模块未解析
  2. 前端 WebSocket 集成 - admin.html Dashboard 改用 WebSocket 替代 30s 轮询,报警页实时通知
  3. 协议层深度统一 - tcp_server.py 辅助方法 (_parse_gps, _parse_datetime 等) 逐步迁移到 protocol/parser.py

调试技巧

# 查看实时日志
tail -f /home/gpsystem/server.log | grep -aE "TCP|login|heartbeat|error|geocod|Amap" --line-buffered

# 检查数据库
python3 -c "
import sqlite3
conn = sqlite3.connect('/home/gpsystem/badge_admin.db')
cur = conn.cursor()
cur.execute('SELECT id, imei, device_type, status, battery_level FROM devices')
for row in cur.fetchall(): print(row)
cur.execute('SELECT id, location_type, latitude, longitude, address, recorded_at FROM location_records ORDER BY id DESC LIMIT 5')
for row in cur.fetchall(): print(row)
"

# 测试 API
curl -s http://localhost:8088/api/devices | python3 -m json.tool
curl -s http://localhost:8088/api/locations/latest/1 | python3 -m json.tool
curl -s http://localhost:8088/health

# 发送指令
curl -s -X POST http://localhost:8088/api/commands/send \
  -H "Content-Type: application/json" \
  -d '{"device_id":1,"command_type":"online_cmd","command_content":"GPSON#"}' | python3 -m json.tool

# 开启蓝牙扫描
curl -s -X POST http://localhost:8088/api/commands/send \
  -H "Content-Type: application/json" \
  -d '{"device_id":1,"command_type":"online_cmd","command_content":"BTON#"}' | python3 -m json.tool

# 管理信标
curl -s http://localhost:8088/api/beacons | python3 -m json.tool
curl -s -X POST http://localhost:8088/api/beacons \
  -H "Content-Type: application/json" \
  -d '{"beacon_mac":"AA:BB:CC:DD:EE:FF","name":"前台","floor":"1F","latitude":30.27,"longitude":120.15}' | python3 -m json.tool

# 检查 FRP 连接
ps aux | grep frpc
# FRP Dashboard: http://152.69.205.186:7500 (admin/PassWord0325)

# 检查端口
python3 -c "
with open('/proc/net/tcp') as f:
    for line in f:
        parts = line.split()
        if len(parts)>=2 and ':' in parts[1]:
            port = int(parts[1].split(':')[1], 16)
            if port in (8088, 5000): print(f'Port {port}: listening')
"

# 测试地理编码
python3 -c "
import asyncio
from app.geocoding import geocode_location, reverse_geocode
async def test():
    lat, lon = await geocode_location(mcc=460, mnc=0, lac=32775, cell_id=205098688)
    print(f'lat={lat}, lon={lon}')
    if lat: print(await reverse_geocode(lat, lon))
asyncio.run(test())
"