初始化毕业设计项目
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
# 项目总文档
|
||||
|
||||
## 1. 项目概览
|
||||
本项目是“人脸识别考勤系统”多端协同工程,包含:
|
||||
- STM32 固件端(设备控制与签到逻辑)
|
||||
- K210 识别端(视觉识别与人脸管理)
|
||||
- C++ 服务端(加密签到、数据管理、同步控制)
|
||||
- Qt 管理端(后台管理与可视化)
|
||||
|
||||
## 2. 推荐运行链路
|
||||
建议使用以下版本组合:
|
||||
1. 服务端:`server/server_encrypted.cpp`(HTTP 8082)
|
||||
2. STM32:`User/main.c`(加密上传 + 时间段同步)
|
||||
3. K210:`k210/main.py`(识别 + 照片上传 + 同步)
|
||||
4. Qt:`QT/AttendanceViewer.pro` 构建的客户端
|
||||
|
||||
## 3. 总体架构
|
||||
1. K210 识别人脸,经 UART 上报 STM32。
|
||||
2. STM32 执行签到策略并加密上传服务端 `POST /api/v1/attendance/checkins`。
|
||||
3. 服务端写入 `attendance.db` 并提供管理 API。
|
||||
4. 服务端在 SQLite 中维护全局同步版本,作为各端同步的权威版本。
|
||||
5. Qt 管理端通过统一同步接口拉取记录/统计、配置、人脸照片和映射。
|
||||
6. STM32 与 K210 分别按本地 `last_version` 增量拉取时间段、人脸映射与待删除任务,保持端云一致。
|
||||
|
||||
## 4. 文档索引(分端)
|
||||
- STM32 固件端:`docs/01_STM32固件端文档.md`
|
||||
- K210 识别端:`docs/02_K210识别端文档.md`
|
||||
- 服务端:`docs/03_服务端文档.md`
|
||||
- Qt 管理端:`docs/04_Qt管理端文档.md`
|
||||
- 项目审查问题清单:`docs/05_项目审查问题清单.md`
|
||||
- 后续优化路线:`docs/07_后续优化路线.md`
|
||||
|
||||
## 5. 各端关键配置位置
|
||||
- STM32 网络与班级配置:`User/main.c`
|
||||
- K210 WiFi/服务器配置:`k210/main.py`
|
||||
- Qt 默认服务地址与 API 封装:`QT/src/ApiClient.cpp`、`QT/src/AppController.cpp`
|
||||
- 服务端端口与数据文件:`server/server_encrypted.cpp`
|
||||
|
||||
## 6. 一致性要求
|
||||
- AES Key/IV 必须在 STM32、服务端、Qt 三端一致。
|
||||
- 服务端建议统一使用加密版本,避免接口行为不一致。
|
||||
- 各端时间配置需统一时区(当前代码以 UTC+8 逻辑处理)。
|
||||
|
||||
## 7. 快速部署顺序
|
||||
1. 编译并启动服务端(确认 `:8082` 可访问)。
|
||||
2. 启动 Qt,先做 `/api/v1/system/health` 连通性验证。
|
||||
3. 烧录 STM32 固件并联网。
|
||||
4. 启动 K210 脚本并确认识别/同步正常。
|
||||
5. 用 Qt 检查签到记录、照片、映射与删除同步链路。
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# STM32固件端文档
|
||||
|
||||
## 1. 端侧职责
|
||||
- 从 K210 串口接收识别结果。
|
||||
- 在本地执行签到逻辑(时间段判断、重复判定、缓存与重传)。
|
||||
- 通过 ESP8266 将签到数据以 AES-128-CBC 加密后上传服务端。
|
||||
- 维护 RTC(DS1302)与网络时间同步。
|
||||
|
||||
对应入口文件:`User/main.c`
|
||||
|
||||
## 2. 关键模块
|
||||
- 主流程:`User/main.c`
|
||||
- 考勤逻辑:`Hardware/Attendance.c`
|
||||
- WiFi/HTTP:`Hardware/esp8266.c`
|
||||
- 加密:`Hardware/Crypto.c`
|
||||
- RTC:`Hardware/DS1302.c`
|
||||
- 光照补光:`Hardware/LightSensor.c`
|
||||
- K210串口解析:`Hardware/K210_USART.c`
|
||||
|
||||
## 3. 当前默认配置(代码内)
|
||||
文件:`User/main.c`
|
||||
- `WIFI_SSID`: `YOUR_WIFI_SSID`
|
||||
- `WIFI_PASSWORD`: `YOUR_WIFI_PASSWORD`
|
||||
- `HTTP_SERVER_IP`: `127.0.0.1`(部署时改为实际服务端地址)
|
||||
- `HTTP_SERVER_PORT`: `8082`
|
||||
- `HTTP_API_TOKEN`: `change-me-token`(部署时必须修改)
|
||||
- `CLASS_ID`: `1`
|
||||
|
||||
建议将上述配置改为部署环境参数后再编译烧录。
|
||||
|
||||
## 4. 通信与接口
|
||||
STM32 通过 `ESP8266_HttpGet` / `ESP8266_SendEncryptedData` 访问服务端:
|
||||
- `POST /api/v1/attendance/checkins`:上传加密签到数据(必带 `X-Encrypted: AES128-CBC`)。
|
||||
- `GET /api/v1/system/time`:获取服务器时间戳。
|
||||
- `GET /api/v1/sync/state?client=stm32&last_version=<version>`:按版本同步时间段配置。
|
||||
- `GET /api/v1/attendance/duplicates`:按时段查重。
|
||||
- `GET /api/v1/attendance/today`:下载今日已签到学号列表。
|
||||
|
||||
## 5. 主流程说明
|
||||
1. 系统初始化(OLED、串口、考勤模块、加密模块、RTC、传感器)。
|
||||
2. 初始化并连接 ESP8266 到 WiFi 与 HTTP 服务器。
|
||||
3. 可用时同步网络时间到 DS1302。
|
||||
4. 定时拉取时间段配置。
|
||||
5. 处理 K210 上报的人脸识别结果:
|
||||
- 置信度通过阈值后进入签到逻辑。
|
||||
- 本地去重 + 服务器时段去重。
|
||||
- 记录加入本地队列并尝试上传。
|
||||
6. 网络异常时离线缓存,恢复后批量补传。
|
||||
|
||||
## 6. 调试建议
|
||||
- 优先看串口调试输出(`DEBUG_PRINT` 系列)。
|
||||
- 检查 `g_wifi_connected`、`g_server_connected` 变化。
|
||||
- 若频繁重连,先确认 AP 稳定性、服务器可达性与 8082 端口。
|
||||
|
||||
## 7. 构建与烧录
|
||||
工程为 Keil 工程:
|
||||
- `Project.uvprojx`
|
||||
- `Project.uvoptx`
|
||||
|
||||
按当前板卡配置在 Keil 中编译并烧录。
|
||||
|
||||
## 8. 依赖与注意事项
|
||||
- 与服务端 AES Key/IV 必须一致(否则 `/api/v1/attendance/checkins` 解密失败)。
|
||||
- DS1302 可用时优先作为本地时钟;不可用则回退网络时间。
|
||||
- `Attendance.c` 的默认时间段仅为兜底,实际以服务器下发为准。
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
# K210识别端文档
|
||||
|
||||
## 1. 端侧职责
|
||||
- 进行人脸检测、特征提取、识别。
|
||||
- 通过 UART1 将识别结果实时发送给 STM32。
|
||||
- 通过 UART2 驱动 ESP8266,与服务端同步人脸映射与删除任务。
|
||||
- 上传新增人脸照片到服务端。
|
||||
|
||||
对应入口文件:`k210/main.py`
|
||||
|
||||
## 2. 通信链路
|
||||
- `UART1`(K210 <-> STM32):发送识别结果。
|
||||
- `UART2`(K210 <-> ESP8266):HTTP 通信到服务端 `:8082`。
|
||||
|
||||
识别结果发送格式(给 STM32):
|
||||
- 已识别:`ID:<id>,NAME:<name>,CONF:<score>`
|
||||
- 陌生人:`UNKNOWN`
|
||||
|
||||
## 3. 本地数据文件
|
||||
- 人脸特征库:`/sd/recordftr.txt`
|
||||
- 本地映射缓存:`/sd/face_mapping.txt`
|
||||
- 本地同步版本:`/sd/sync_version.txt`
|
||||
- 本地人脸照片缓存:`/sd/face_photos/<face_id>.jpg`
|
||||
|
||||
## 4. 服务端交互接口
|
||||
K210 在脚本中使用 HTTP 访问以下接口:
|
||||
- `GET /api/v1/sync/state?client=k210&last_version=<本地版本>`:按版本拉取统一同步状态。
|
||||
- `POST /api/v1/sync/ack`:确认已完成删除任务。
|
||||
- `GET /api/v1/sync/request`:轮询是否触发同步。
|
||||
- `POST /api/v1/sync/request/ack`:确认收到同步请求。
|
||||
- `GET /api/v1/faces/photos`:获取服务端已有照片列表,用于判断本地照片是否需要补传。
|
||||
- `POST /api/v1/faces/photos/<face_id>`:上传 JPEG 人脸照片。
|
||||
|
||||
v1 兼容辅助接口:
|
||||
- `GET /api/v1/faces/mappings`
|
||||
- `GET /api/v1/faces/pending-delete`
|
||||
- `POST /api/v1/faces/delete-ack`
|
||||
|
||||
## 5. 启动与运行流程
|
||||
1. 初始化摄像头、KPU 模型、LCD、按键中断、串口。
|
||||
2. 启动 ESP8266,尝试连接 WiFi。
|
||||
3. 启动同步:
|
||||
- 读取 `/sd/sync_version.txt`。
|
||||
- 请求统一同步状态。
|
||||
- 先删除 `pending_delete` 中的人脸特征。
|
||||
- 再应用 `face_mappings`。
|
||||
- 成功后保存新的同步版本。
|
||||
4. 进入主循环:识别 + 绘制 + 向 STM32 上报。
|
||||
5. 分时网络任务(避免阻塞识别帧率):
|
||||
- 新增人脸时先把 JPEG 保存到 `/sd/face_photos/`,再加入上传队列。
|
||||
- 启动时先获取服务端照片列表,再扫描 `/sd/face_photos/`,只补传服务端缺失的照片;如果服务端不可达,则退回全量加入补传队列。
|
||||
- 优先处理照片上传队列,上传失败会保留本地照片并等待下次重试。
|
||||
- 然后处理同步任务。
|
||||
|
||||
## 6. 按键功能
|
||||
在 `k210/main.py` 中定义:
|
||||
- 短按:添加人脸。
|
||||
- 长按(约2秒):进入删除单个人脸模式。
|
||||
- 超长按(约5秒):删除全部人脸。
|
||||
|
||||
## 7. 当前默认配置(代码内)
|
||||
- WiFi:`YOUR_WIFI_SSID / YOUR_WIFI_PASSWORD`
|
||||
- 服务端:`127.0.0.1:8082`(部署时改为实际服务端地址)
|
||||
- API Token:`change-me-token`(部署时必须修改)
|
||||
|
||||
文件位置:`k210/main.py`
|
||||
|
||||
## 8. 部署注意事项
|
||||
- SD 卡需包含模型文件:
|
||||
- `/sd/FaceDetection.smodel`
|
||||
- `/sd/FaceLandmarkDetection.smodel`
|
||||
- `/sd/FeatureExtraction.smodel`
|
||||
- 与 STM32 使用统一学号语义(映射同步后应发送真实学号语义)。
|
||||
- 网络任务失败不会阻塞识别主流程,但会影响照片与映射同步时效;本地照片缓存会用于后续补传。
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
# 服务端文档
|
||||
|
||||
## 1. 推荐服务端版本
|
||||
当前项目推荐使用:`server/server_encrypted.cpp`
|
||||
|
||||
原因:
|
||||
- 支持 STM32 上传加密签到数据。
|
||||
- 支持 Qt 管理端所需管理 API。
|
||||
- 支持 K210 人脸照片与映射同步相关 API。
|
||||
|
||||
## 2. 端口与数据文件
|
||||
- HTTP 端口:`8082`
|
||||
- SQLite 数据库:`attendance.db`
|
||||
- 日志:运行时生成 `attendance.log`
|
||||
- 旧数据迁移来源:`attendance_database.csv`、`timeslots_config.json`、`mapping_config.json`、`face_id_mapping.json`、`pending_delete.json`、`sync_request.json`、`sync_version.txt`
|
||||
- 人脸目录:`faces/`
|
||||
- 全局同步版本:存储在 `attendance.db` 的 `kv_store` 表中。
|
||||
|
||||
## 3. 签到数据链路
|
||||
1. STM32 调用 `POST /api/v1/attendance/checkins` 上传 Base64 密文。
|
||||
2. 服务端按 AES-128-CBC 解密并解析 JSON。
|
||||
3. 将记录写入 `attendance.db` 的 `records` 表(含 `class_id` 字段)。
|
||||
|
||||
## 4. 核心 API(按模块)
|
||||
### 4.1 考勤与统计
|
||||
- `POST /api/v1/attendance/checkins`
|
||||
- `GET /api/v1/attendance/records?limit=&offset=`
|
||||
- `GET /api/v1/attendance/statistics`
|
||||
- `POST /api/v1/attendance/records/clear`
|
||||
- `DELETE /api/v1/attendance/records?timestamp=&student_id=`
|
||||
- `GET /api/v1/system/health`
|
||||
- `GET /api/v1/system/storage`
|
||||
- `GET /api/v1/system/time`
|
||||
|
||||
说明:
|
||||
- `/api/v1/attendance/records` 与 `/api/v1/attendance/statistics` 默认返回明文 JSON;请求头包含 `X-Encrypted-Response: true` 时返回加密数据。
|
||||
|
||||
### 4.2 时间段与查重
|
||||
- `POST /api/v1/config/time-slots`
|
||||
- `GET /api/v1/config/time-slots`
|
||||
- `GET /api/v1/attendance/duplicates?student_id=&slot_start=&slot_end=`
|
||||
- `GET /api/v1/attendance/today?slot_start=&slot_end=`
|
||||
|
||||
### 4.3 映射与照片
|
||||
- `POST /api/v1/config/mappings`
|
||||
- `GET /api/v1/config/mappings`
|
||||
- `GET /api/v1/faces/photos`
|
||||
- `GET /api/v1/faces/photos/<id>`
|
||||
- `POST /api/v1/faces/photos/<face_id>`
|
||||
- `POST /api/v1/faces/mappings`
|
||||
- `GET /api/v1/faces/mappings`
|
||||
- `GET /api/v1/faces/student-ids`
|
||||
|
||||
### 4.4 统一同步接口(推荐)
|
||||
- `GET /api/v1/sync/state?client=<client>&last_version=<version>`
|
||||
- `POST /api/v1/sync/ack`
|
||||
- `GET /api/v1/sync/version`
|
||||
|
||||
说明:
|
||||
- 服务端是唯一权威状态源。
|
||||
- 每次签到、清空/删除记录、修改时间段、修改映射、上传照片、删除人脸,服务端都会递增 `kv_store.sync_version`。
|
||||
- 设备端保存自己的 `last_version`,请求时带给服务端。
|
||||
- 如果版本无变化,服务端返回 `changed:false`,设备端无需重复解析配置。
|
||||
- 如果版本有变化,服务端返回 `changed:true` 和当前客户端需要的状态。
|
||||
- `/api/v1/sync/version` 只返回当前版本和服务端时间,适合调试时快速确认同步版本。
|
||||
|
||||
客户端示例:
|
||||
- `client=k210`:返回 `face_mappings` 和 `pending_delete`。
|
||||
- `client=stm32`:返回 `time_slots` 和 `server_time`。
|
||||
- `client=qt`:返回时间段、人脸映射、照片列表、学生ID列表、记录、统计和映射配置。
|
||||
- `client=all`:返回全部可同步状态。
|
||||
|
||||
K210 删除确认示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"client": "k210",
|
||||
"version": 12,
|
||||
"deleted": ["2", "3"]
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5 人脸删除与同步控制
|
||||
- `DELETE /api/v1/faces/<student_id>?delete_records=true`
|
||||
- `GET /api/v1/faces/pending-delete`
|
||||
- `POST /api/v1/faces/delete-ack`
|
||||
- `GET /api/v1/sync/request`
|
||||
- `POST /api/v1/sync/request/ack`
|
||||
- `POST /api/v1/sync/trigger`
|
||||
|
||||
说明:这些是 v1 下的兼容控制接口;推荐设备端优先使用统一同步接口,只有手动调试或兼容旧流程时才单独调用。
|
||||
|
||||
### 4.6 旧接口兼容
|
||||
服务端内部仍保留旧 `/api/...` 路由兼容层,便于历史程序临时访问;新代码和新文档统一使用 `/api/v1/...`。
|
||||
|
||||
## 5. 编译与运行
|
||||
Windows(示例):
|
||||
```bash
|
||||
cd server
|
||||
g++ -o server_encrypted.exe server_encrypted.cpp -lws2_32 -lcrypto -lssl -lsqlite3 -std=c++11
|
||||
server_encrypted.exe
|
||||
```
|
||||
|
||||
Linux(示例):
|
||||
```bash
|
||||
cd server
|
||||
g++ -o server_encrypted server_encrypted.cpp -lcrypto -lssl -lsqlite3 -lpthread -std=c++11
|
||||
./server_encrypted
|
||||
```
|
||||
|
||||
说明:服务端启动时会自动创建 `attendance.db`。如果检测到旧 CSV/JSON 文件,会在首次启动时导入;之后以后端 API 和 SQLite 为准。
|
||||
|
||||
## 6. 版本说明
|
||||
仓库仅保留当前推荐版本:`server_encrypted.cpp`。旧版实验服务端与测试脚本已清理,避免误部署非鉴权接口。
|
||||
|
||||
## 7. 关键一致性要求
|
||||
- AES Key/IV 必须与 STM32、Qt 完全一致。
|
||||
- 若 Qt 无法解析记录,先检查是否误连到非加密版本服务端。
|
||||
|
||||
## 8. 后续优化
|
||||
服务端当前已经完成 SQLite 主存储、v1 接口和统一同步模型。后续如需继续增强 JSON 解析、同步版本拆分、删除任务 task_id 化、数据库备份或 Qt 同步状态面板,参考 `docs/07_后续优化路线.md`。
|
||||
|
||||
@@ -0,0 +1,628 @@
|
||||
# Qt管理端当前功能文档
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
本文档用于说明 `QT/` 管理端重构后的实际架构、当前已经实现的功能、服务端接口依赖、构建方式和已知限制。
|
||||
|
||||
本文结论以当前源码为准,不再沿用旧版 Widgets 管理端中 `MainWindow`、`NetworkApiManager`、多个独立 `Dialog` 的描述。
|
||||
|
||||
## 2. 模块定位
|
||||
|
||||
Qt 管理端是人脸识别考勤系统的跨平台管理客户端,主要负责:
|
||||
|
||||
- 从服务端拉取考勤记录、统计信息和配置数据。
|
||||
- 查看、筛选、导出考勤记录。
|
||||
- 按学生维度生成本地统计报表。
|
||||
- 管理服务端地址、签到时间段、学生姓名映射、班级名称映射。
|
||||
- 管理 K210 上传的人脸照片和人脸 ID 到学号的绑定关系。
|
||||
- 在服务端不可达时给出简短状态提示,并自动重连。
|
||||
|
||||
当前 Qt 端已经重构为 Qt 6 Quick/QML 架构,主要源码位置如下:
|
||||
|
||||
| 模块 | 路径 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 程序入口 | `QT/main.cpp` | 初始化应用信息、图标、QML 引擎和 `AppController` |
|
||||
| 主界面 | `QT/qml/Main.qml` | 单窗口 QML 页面,包含记录、统计、配置、人脸、关于五个页面 |
|
||||
| 业务中枢 | `QT/src/AppController.*` | 连接 QML 和业务逻辑,负责筛选、统计、配置保存、重连调度 |
|
||||
| 网络接口 | `QT/src/ApiClient.*` | 封装 HTTP API、请求超时、加密响应解密和 JSON 解析 |
|
||||
| 数据结构 | `QT/src/AttendanceTypes.h` | 考勤记录、统计项、签到时段等结构定义 |
|
||||
| 列表模型 | `QT/src/*ListModel.*` | 向 QML 暴露记录、统计、时间段、映射、人脸列表 |
|
||||
| 加密工具 | `QT/src/CryptoUtils.*` | AES-128-CBC Base64 响应解密 |
|
||||
| AES 实现 | `QT/src/crypto/tiny_aes.hpp` | AES 底层实现 |
|
||||
| 构建配置 | `QT/CMakeLists.txt` | Qt 6 CMake 构建、资源、图标、Android 配置 |
|
||||
| 平台资源 | `QT/assets/`、`QT/android/`、`QT/cmake/` | 桌面图标、Android 图标与平台模板 |
|
||||
|
||||
## 3. 技术栈与界面结构
|
||||
|
||||
当前客户端使用:
|
||||
|
||||
- C++17
|
||||
- Qt 6.2+
|
||||
- Qt Quick / Qt Quick Controls 2
|
||||
- Qt Core、Gui、Qml、Quick、QuickControls2、Network
|
||||
- CMake 3.16+
|
||||
|
||||
界面由 `ApplicationWindow` 承载,内部使用 `StackLayout` 切换五个页面:
|
||||
|
||||
- 考勤记录
|
||||
- 统计分析
|
||||
- 配置中心
|
||||
- 人脸管理
|
||||
- 关于
|
||||
|
||||
QML 不直接访问网络。所有页面操作统一调用 `AppController` 暴露的 `Q_INVOKABLE` 方法和 `Q_PROPERTY` 状态,数据表格通过 `RecordListModel`、`StatisticsListModel`、`TimeSlotListModel`、`MappingListModel`、`FaceListModel` 渲染。
|
||||
|
||||
## 4. 当前已实现功能
|
||||
|
||||
### 4.1 启动与服务连接
|
||||
|
||||
应用启动流程:
|
||||
|
||||
1. `main.cpp` 创建 `QGuiApplication`。
|
||||
2. 设置应用名称、版本、组织名和窗口图标。
|
||||
3. Android 平台启用原生文字渲染,改善中文粗体显示。
|
||||
4. 创建 `AppController`。
|
||||
5. 将 `appController` 注入 QML 上下文。
|
||||
6. 加载 `qrc:/qml/Main.qml`。
|
||||
|
||||
`AppController` 构造时会:
|
||||
|
||||
- 读取本地服务端地址、映射配置和时间段配置。
|
||||
- 设置 `ApiClient` 的服务端地址。
|
||||
- 启动健康检查。
|
||||
- 如果服务端可用,同步记录、统计、时间段和映射配置。
|
||||
- 如果服务端不可用,进入自动重连流程。
|
||||
|
||||
默认服务端地址:
|
||||
|
||||
```text
|
||||
http://127.0.0.1:8082
|
||||
```
|
||||
|
||||
用户可以在“配置中心”修改服务端地址。保存后会写入 `QSettings` 的 `server/url`,下次启动会读取并使用。
|
||||
|
||||
### 4.2 自动重连与状态提示
|
||||
|
||||
当前实现了基于 `/api/v1/system/health` 的连接检查:
|
||||
|
||||
- 网络请求超时时间为 5 秒。
|
||||
- 服务端不可达时显示简短提示:`服务器未连接,稍后重试`。
|
||||
- 自动重连间隔为 5 秒。
|
||||
- 重连期间显示:`正在重连服务器`。
|
||||
- 服务恢复后自动刷新记录、统计、时间段、映射和人脸相关数据。
|
||||
|
||||
底部 Toast 会展示状态信息,避免把底层网络错误、URL 和长异常文本直接暴露给用户。
|
||||
|
||||
### 4.3 考勤记录查看
|
||||
|
||||
“考勤记录”页用于查看服务端同步下来的签到记录。
|
||||
|
||||
当前实现:
|
||||
|
||||
- 默认请求最新 `1000` 条记录。
|
||||
- 顶部显示总记录数、今日签到数、筛选结果数。
|
||||
- 记录按签到时间倒序显示。
|
||||
- 支持刷新记录。
|
||||
- 支持清空服务端全部考勤记录。
|
||||
- 支持导出当前筛选结果为 CSV。
|
||||
|
||||
记录表格字段:
|
||||
|
||||
- 签到时间
|
||||
- 班级
|
||||
- 学号
|
||||
- 姓名
|
||||
- 置信度
|
||||
- 状态
|
||||
|
||||
显示规则:
|
||||
|
||||
- 班级优先使用“班级编号 -> 班级名称”映射;未配置时显示原班级编号。
|
||||
- 姓名优先使用“学号 -> 姓名”映射;未配置时回退到服务端记录中的原始姓名。
|
||||
- 状态会被归一化判断是否成功,成功和失败在界面上使用不同样式显示。
|
||||
|
||||
### 4.4 记录筛选
|
||||
|
||||
记录页支持本地筛选。筛选条件包括:
|
||||
|
||||
- 班级
|
||||
- 学号
|
||||
- 姓名关键字
|
||||
- 日期范围
|
||||
|
||||
日期范围支持:
|
||||
|
||||
- 全部时间
|
||||
- 今天
|
||||
- 最近 7 天
|
||||
- 最近 30 天
|
||||
- 指定日期
|
||||
|
||||
筛选基于当前已经加载到客户端内存中的记录执行,不会为每次筛选重新请求服务端。
|
||||
|
||||
### 4.5 记录导出与清空
|
||||
|
||||
当前筛选结果可以导出为 CSV 文件。
|
||||
|
||||
导出内容包括:
|
||||
|
||||
```text
|
||||
# 考勤记录导出
|
||||
# 导出时间: yyyy-MM-dd HH:mm:ss
|
||||
# 记录总数: N
|
||||
签到时间,班级编号,班级名称,学号,姓名,置信度,状态
|
||||
```
|
||||
|
||||
“清空记录”会调用服务端 `/api/v1/attendance/records/clear`。界面在执行前会弹出确认对话框,并提示该操作不可恢复。
|
||||
|
||||
需要注意:当前界面只开放“清空全部记录”,没有在记录表中开放单条删除或批量删除入口。
|
||||
|
||||
### 4.6 统计分析
|
||||
|
||||
“统计分析”页基于当前加载到客户端的考勤记录,在本地按学生维度计算统计结果。
|
||||
|
||||
支持:
|
||||
|
||||
- 快捷选择统计范围。
|
||||
- 手动输入开始日期和结束日期。
|
||||
- 显示统计学生数、统计记录数、统计范围。
|
||||
- 导出统计报表 CSV。
|
||||
|
||||
统计字段:
|
||||
|
||||
- 学号
|
||||
- 姓名
|
||||
- 总签到次数
|
||||
- 今日签到次数
|
||||
- 最后签到时间
|
||||
- 平均置信度
|
||||
|
||||
导出字段:
|
||||
|
||||
```text
|
||||
学号,姓名,总签到次数,今日签到,最后签到时间,平均置信度
|
||||
```
|
||||
|
||||
统计结果不是服务端全量统计,而是对当前客户端已经拉取的最多 `1000` 条记录进行本地计算。
|
||||
|
||||
### 4.7 配置中心
|
||||
|
||||
“配置中心”集中管理服务连接、签到时间段和名称映射。
|
||||
|
||||
#### 服务端地址
|
||||
|
||||
支持:
|
||||
|
||||
- 查看当前服务端地址。
|
||||
- 修改并保存服务端地址。
|
||||
- 测试服务端连接。
|
||||
|
||||
保存后会写入本地 `QSettings`:
|
||||
|
||||
```text
|
||||
server/url
|
||||
```
|
||||
|
||||
#### 签到时间段
|
||||
|
||||
支持:
|
||||
|
||||
- 查看当前时间段。
|
||||
- 新增时间段。
|
||||
- 编辑时间段。
|
||||
- 删除时间段。
|
||||
- 恢复默认时间段。
|
||||
- 从服务端同步时间段。
|
||||
- 保存时间段到本地并上传服务端。
|
||||
|
||||
默认时间段:
|
||||
|
||||
| 名称 | 开始时间 | 结束时间 | 默认状态 |
|
||||
| --- | --- | --- | --- |
|
||||
| 上午签到 | 08:00 | 09:30 | 启用 |
|
||||
| 中午签退 | 12:00 | 13:00 | 启用 |
|
||||
| 下午签到 | 14:00 | 15:00 | 启用 |
|
||||
| 晚上签退 | 17:00 | 18:30 | 启用 |
|
||||
|
||||
本地文件:
|
||||
|
||||
```text
|
||||
timeslot_config.json
|
||||
```
|
||||
|
||||
#### 名称映射
|
||||
|
||||
支持两类映射:
|
||||
|
||||
- 学号 -> 姓名
|
||||
- 班级编号 -> 班级名称
|
||||
|
||||
支持操作:
|
||||
|
||||
- 新增映射。
|
||||
- 直接编辑映射键和值。
|
||||
- 删除映射。
|
||||
- 从服务端加载映射配置。
|
||||
- 保存到本地并上传服务端。
|
||||
|
||||
本地文件:
|
||||
|
||||
```text
|
||||
mapping_config.json
|
||||
```
|
||||
|
||||
映射会影响记录列表和统计报表中的姓名、班级显示。
|
||||
|
||||
### 4.8 人脸管理
|
||||
|
||||
“人脸管理”页用于维护 K210 上传的人脸照片和人脸 ID 到学号的绑定关系。
|
||||
|
||||
当前实现:
|
||||
|
||||
- 从服务端同步人脸照片 ID。
|
||||
- 从服务端同步全部学生 ID。
|
||||
- 从服务端同步人脸 ID 到学号的映射。
|
||||
- 合并照片 ID 和学生 ID,生成统一管理列表。
|
||||
- 显示照片或“无照片”状态。
|
||||
- 编辑人脸 ID 对应的学号。
|
||||
- 保存人脸映射到服务端。
|
||||
- 删除指定人脸。
|
||||
- 删除时可选择同时删除该学生相关考勤记录。
|
||||
|
||||
照片加载方式:
|
||||
|
||||
```text
|
||||
ApiClient -> GET /api/v1/faces/photos/{id} -> 本地 file:// 缓存 -> QML Image
|
||||
```
|
||||
|
||||
这样可以在请求照片时携带 `X-Api-Token`,避免 QML `Image` 直接访问受鉴权接口导致照片加载失败。
|
||||
|
||||
### 4.9 关于页面
|
||||
|
||||
“关于”页展示:
|
||||
|
||||
- 系统简介。
|
||||
- 功能范围。
|
||||
- 毕业设计说明。
|
||||
- 作者和仓库信息。
|
||||
|
||||
当前关于页是 QML 页面的一部分,不再是独立窗口。
|
||||
|
||||
### 4.10 桌面端和移动端适配
|
||||
|
||||
当前 QML 已对窄屏进行适配:
|
||||
|
||||
- `phoneLayout` 用于手机宽度布局。
|
||||
- 顶部导航在窄屏下使用紧凑按钮。
|
||||
- 筛选区、统计卡片和配置区会根据宽度调整列数。
|
||||
- 表格在窄屏下隐藏部分非关键列或调整宽度。
|
||||
- Android 端使用原生文字渲染并降低粗体权重,减少中文粗体重影。
|
||||
|
||||
## 5. 服务端 API 依赖
|
||||
|
||||
客户端基于服务端地址拼接 `/api` 前缀访问接口。刷新全部数据时优先使用统一同步接口:
|
||||
|
||||
| 方法 | 路径 | 用途 |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/api/v1/sync/state?client=qt&last_version={version}` | 按版本同步 Qt 所需完整状态 |
|
||||
|
||||
服务端版本无变化时返回 `changed:false`,Qt 不再重复拉取全部数据。局部刷新和保存操作使用下列 v1 子接口。
|
||||
|
||||
| 方法 | 路径 | 用途 |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/api/v1/attendance/records?limit={limit}&offset={offset}` | 获取考勤记录 |
|
||||
| `GET` | `/api/v1/attendance/statistics` | 获取总数、今日数和服务端时间 |
|
||||
| `POST` | `/api/v1/attendance/records/clear` | 清空全部记录 |
|
||||
| `DELETE` | `/api/v1/attendance/records?timestamp={timestamp}&student_id={studentId}` | 删除单条记录,当前 API 层保留,界面未开放 |
|
||||
| `GET` | `/api/v1/config/time-slots` | 获取签到时段 |
|
||||
| `POST` | `/api/v1/config/time-slots` | 保存签到时段 |
|
||||
| `GET` | `/api/v1/config/mappings` | 获取学生和班级映射 |
|
||||
| `POST` | `/api/v1/config/mappings` | 保存学生和班级映射 |
|
||||
| `GET` | `/api/v1/faces/mappings` | 获取人脸 ID 到学号映射 |
|
||||
| `POST` | `/api/v1/faces/mappings` | 保存人脸 ID 到学号映射 |
|
||||
| `GET` | `/api/v1/faces/photos` | 获取照片 ID 列表 |
|
||||
| `GET` | `/api/v1/faces/student-ids` | 获取所有学生 ID |
|
||||
| `GET` | `/api/v1/faces/photos/{id}` | 获取人脸照片 |
|
||||
| `DELETE` | `/api/v1/faces/{studentId}` | 删除人脸信息 |
|
||||
| `DELETE` | `/api/v1/faces/{studentId}?delete_records=true` | 删除人脸信息并删除相关考勤记录 |
|
||||
| `GET` | `/api/v1/system/health` | 健康检查 |
|
||||
|
||||
### 5.1 考勤记录格式
|
||||
|
||||
`/api/v1/attendance/records` 解密后应返回 JSON 数组,单条记录示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"timestamp": "2026-04-24 08:10:23",
|
||||
"student_id": "20240001",
|
||||
"name": "张三",
|
||||
"confidence": 0.9821,
|
||||
"status": "success",
|
||||
"class_id": "1"
|
||||
}
|
||||
```
|
||||
|
||||
字段处理规则:
|
||||
|
||||
- `timestamp` 需要符合 `yyyy-MM-dd HH:mm:ss`。
|
||||
- `student_id` 不能为空。
|
||||
- `class_id` 为空时客户端按 `"0"` 处理。
|
||||
- `status` 可以是字符串、布尔值或数字,客户端会统一转换为显示状态。
|
||||
- `timestamp` 以 `0000-` 开头的记录会被忽略。
|
||||
|
||||
### 5.2 统计数据格式
|
||||
|
||||
```json
|
||||
{
|
||||
"total": 120,
|
||||
"today": 8,
|
||||
"server_time": "2026-04-24 10:30:00"
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 签到时段格式
|
||||
|
||||
```json
|
||||
{
|
||||
"time_slots": [
|
||||
{
|
||||
"name": "上午签到",
|
||||
"start_time": "08:00",
|
||||
"end_time": "09:30",
|
||||
"enabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 5.4 映射配置格式
|
||||
|
||||
```json
|
||||
{
|
||||
"student_name_map": {
|
||||
"20240001": "张三"
|
||||
},
|
||||
"class_name_map": {
|
||||
"1": "软件工程一班"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.5 人脸映射格式
|
||||
|
||||
```json
|
||||
{
|
||||
"mappings": {
|
||||
"face_001": "20240001"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 6. 加密响应
|
||||
|
||||
客户端对 `/api/v1/attendance/records` 和 `/api/v1/attendance/statistics` 请求添加请求头:
|
||||
|
||||
```text
|
||||
X-Encrypted-Response: true
|
||||
```
|
||||
|
||||
客户端期望服务端返回 Base64 编码的 AES-128-CBC 密文。收到响应后处理流程为:
|
||||
|
||||
1. Base64 解码。
|
||||
2. AES-128-CBC 解密。
|
||||
3. PKCS#7 去填充。
|
||||
4. 解析 JSON。
|
||||
|
||||
加密参数位于 `QT/src/CryptoUtils.cpp`:
|
||||
|
||||
| 参数 | 值 |
|
||||
| --- | --- |
|
||||
| 算法 | AES-128-CBC |
|
||||
| Padding | PKCS#7 |
|
||||
| Key | `2b 7e 15 16 28 ae d2 a6 ab f7 15 88 09 cf 4f 3c` |
|
||||
| IV | `00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f` |
|
||||
|
||||
如果服务端没有按加密格式返回,记录和统计接口会解析失败。
|
||||
|
||||
## 7. 本地配置持久化
|
||||
|
||||
应用使用 `QSettings` 和 `QStandardPaths::AppDataLocation` 保存本地状态。
|
||||
|
||||
应用信息:
|
||||
|
||||
```text
|
||||
ApplicationName: 人脸识别考勤系统
|
||||
ApplicationVersion: 3.0.0
|
||||
OrganizationName: STM32 Face Recognition
|
||||
```
|
||||
|
||||
本地保存内容:
|
||||
|
||||
| 配置 | 保存方式 |
|
||||
| --- | --- |
|
||||
| 服务端地址 | `QSettings`,键名 `server/url` |
|
||||
| 学生姓名映射和班级名称映射 | `mapping_config.json` |
|
||||
| 签到时段配置 | `timeslot_config.json` |
|
||||
|
||||
`mapping_config.json` 和 `timeslot_config.json` 位于 Qt 返回的 `AppDataLocation` 目录中,不在项目源码目录内。
|
||||
|
||||
## 8. 构建与运行
|
||||
|
||||
### 8.1 推荐构建方式
|
||||
|
||||
当前推荐使用 CMake 构建。旧版 `qmake` / `.pro` 文档不再适用于重构后的 Qt Quick 版本。
|
||||
|
||||
基础要求:
|
||||
|
||||
- Qt 6.2 或更高版本
|
||||
- CMake 3.16 或更高版本
|
||||
- 支持 C++17 的编译器
|
||||
|
||||
Windows 桌面端推荐使用 Qt 6.x MinGW 64-bit Kit。
|
||||
|
||||
### 8.2 Qt Creator 构建
|
||||
|
||||
1. 打开 Qt Creator。
|
||||
2. 打开 `QT/CMakeLists.txt`。
|
||||
3. 选择 Desktop Qt 6.x 或 Android Kit。
|
||||
4. 等待 CMake 配置完成。
|
||||
5. 构建目标 `apprewrite_qt`。
|
||||
6. 桌面端直接运行;Android 端部署到设备或模拟器。
|
||||
|
||||
### 8.3 命令行构建
|
||||
|
||||
Windows 示例:
|
||||
|
||||
```powershell
|
||||
cmake -S QT -B QT/build -DCMAKE_PREFIX_PATH=D:/Qt/6.10.0/mingw_64
|
||||
cmake --build QT/build --target apprewrite_qt
|
||||
```
|
||||
|
||||
Linux 示例:
|
||||
|
||||
```bash
|
||||
cmake -S QT -B QT/build -DCMAKE_PREFIX_PATH=/path/to/Qt/6.x/gcc_64
|
||||
cmake --build QT/build --target apprewrite_qt
|
||||
```
|
||||
|
||||
安装:
|
||||
|
||||
```bash
|
||||
cmake --install QT/build
|
||||
```
|
||||
|
||||
当前 `QT/CMakeLists.txt` 将运行产物输出到:
|
||||
|
||||
```text
|
||||
QT/dist
|
||||
```
|
||||
|
||||
### 8.4 Windows 独立运行
|
||||
|
||||
直接双击 `apprewrite_qt.exe` 时,如果提示缺少 `Qt6Gui.dll`、`Qt6Core.dll` 等运行库,需要使用和构建 Kit 一致的 `windeployqt` 收集依赖。
|
||||
|
||||
示例:
|
||||
|
||||
```powershell
|
||||
D:\Qt\6.10.0\mingw_64\bin\windeployqt.exe QT\dist\apprewrite_qt.exe
|
||||
```
|
||||
|
||||
如果路径包含中文导致部署工具异常,可以把 exe 复制到纯英文路径后部署。
|
||||
|
||||
### 8.5 Android 构建
|
||||
|
||||
Android 相关配置位于 `QT/CMakeLists.txt`:
|
||||
|
||||
```cmake
|
||||
QT_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android"
|
||||
QT_ANDROID_APP_NAME "${REWRITE_QT_DISPLAY_NAME}"
|
||||
QT_ANDROID_APP_ICON "@mipmap/ic_launcher"
|
||||
```
|
||||
|
||||
补充配置位于 `QT/android/gradle.properties`:
|
||||
|
||||
```properties
|
||||
android.overridePathCheck=true
|
||||
android.useAndroidX=true
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- `android.overridePathCheck=true` 用于绕过 Windows 下 Android Gradle 对中文路径的检查。
|
||||
- `android.useAndroidX=true` 用于启用 AndroidX。
|
||||
- Android 应用显示名为“人脸识别考勤系统”。
|
||||
- Android 启动图标位于 `QT/android/res/mipmap-*`。
|
||||
|
||||
## 9. 图标与资源
|
||||
|
||||
桌面图标资源位于:
|
||||
|
||||
```text
|
||||
QT/assets/favicon.ico
|
||||
QT/assets/favicon-16x16.png
|
||||
QT/assets/favicon-32x32.png
|
||||
QT/assets/favicon-48x48.png
|
||||
QT/assets/favicon-64x64.png
|
||||
QT/assets/favicon-128x128.png
|
||||
QT/assets/favicon-192x192.png
|
||||
QT/assets/favicon-256x256.png
|
||||
```
|
||||
|
||||
Android 启动图标位于:
|
||||
|
||||
```text
|
||||
QT/android/res/mipmap-mdpi/ic_launcher.png
|
||||
QT/android/res/mipmap-hdpi/ic_launcher.png
|
||||
QT/android/res/mipmap-xhdpi/ic_launcher.png
|
||||
QT/android/res/mipmap-xxhdpi/ic_launcher.png
|
||||
QT/android/res/mipmap-xxxhdpi/ic_launcher.png
|
||||
```
|
||||
|
||||
Linux 桌面文件模板位于:
|
||||
|
||||
```text
|
||||
QT/cmake/rewrite_qt.desktop.in
|
||||
```
|
||||
|
||||
Windows 图标资源模板位于:
|
||||
|
||||
```text
|
||||
QT/cmake/app_icon.rc.in
|
||||
```
|
||||
|
||||
## 10. 当前已知限制
|
||||
|
||||
### 10.1 记录和统计最多基于当前加载的 1000 条记录
|
||||
|
||||
`AppController::refreshRecords()` 当前固定调用:
|
||||
|
||||
```cpp
|
||||
m_apiClient.fetchRecords(1000, 0);
|
||||
```
|
||||
|
||||
因此记录列表、筛选结果和本地统计都只覆盖客户端当前加载的记录。如果服务端历史记录超过 `1000` 条,统计页不等于全量历史统计。
|
||||
|
||||
### 10.2 筛选和统计为本地计算
|
||||
|
||||
班级、学号、姓名、日期筛选以及统计分析都在客户端内存中完成,不会向服务端传递筛选条件。
|
||||
|
||||
优点是交互响应快;限制是数据范围受当前加载记录影响。
|
||||
|
||||
### 10.3 单条删除 API 保留但界面未开放
|
||||
|
||||
`ApiClient` 中保留了 `deleteRecord()` 和 `/api/v1/attendance/records` 调用,`AppController` 也连接了删除完成信号,但当前 QML 记录表没有提供单条删除或批量删除入口。
|
||||
|
||||
因此当前对外说明时,不应把“单条记录删除”或“批量删除记录”描述为已开放功能。
|
||||
|
||||
### 10.4 自动刷新不是定时拉取记录
|
||||
|
||||
当前代码实现的是服务端不可达后的自动重连检查,不是固定周期自动刷新记录。
|
||||
|
||||
用户需要手动点击“刷新”或“刷新全部数据”同步最新数据;服务恢复连接时也会自动刷新。
|
||||
|
||||
### 10.5 时间输入缺少强校验
|
||||
|
||||
签到时间段编辑框使用字符串输入,代码按 `HH:mm` 解析。如果输入非法时间,模型层不会弹出明确错误提示,可能产生无效时间值。后续可补充输入掩码或校验提示。
|
||||
|
||||
### 10.6 CSV 导出未做字段转义
|
||||
|
||||
导出逻辑直接用逗号拼接字段。如果姓名、班级名或状态中包含逗号、换行或引号,CSV 可能被表格软件解析成错误列。后续可补充标准 CSV 转义函数。
|
||||
|
||||
### 10.7 照片 URL 依赖服务端静态接口
|
||||
|
||||
人脸照片由 `ApiClient` 通过 `/api/v1/faces/photos/{id}` 携带鉴权头下载,并缓存为本地 `file://` 地址后交给 QML 显示。如果服务端返回格式或图片 ID 与学生 ID 规则变化,客户端的人脸列表合并逻辑需要同步调整。
|
||||
|
||||
## 11. 当前结论
|
||||
|
||||
重构后的 Qt 管理端已经从旧版 Widgets 多窗口结构迁移为 Qt 6 Quick/QML 单窗口客户端。当前核心链路已经完整:
|
||||
|
||||
- 服务端连接与健康检查。
|
||||
- 加密记录和统计接口访问。
|
||||
- 考勤记录查看、筛选、导出、清空。
|
||||
- 本地统计分析和统计导出。
|
||||
- 服务端地址、时间段、学生映射、班级映射配置。
|
||||
- 人脸照片、人脸映射和删除管理。
|
||||
- Windows 桌面端与 Android 端构建配置。
|
||||
|
||||
当前主要不足集中在数据分页、全量统计、记录级删除入口、输入校验和 CSV 转义。后续如果要提升为更完整的管理端,应优先补充分页加载或服务端筛选统计接口,再补齐记录级管理操作。
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
# 项目审查问题清单
|
||||
|
||||
审查日期:2026-05-01
|
||||
|
||||
本文档用于记录当前项目中已发现的高风险问题,并作为后续逐项修复的跟踪清单。状态说明:
|
||||
|
||||
- `待处理`:尚未开始修复。
|
||||
- `处理中`:已经开始改动或验证。
|
||||
- `已修复`:代码已修复并通过基本验证。
|
||||
- `暂缓`:因演示环境、硬件依赖或其他原因暂不处理。
|
||||
|
||||
## 1. 优先级概览
|
||||
|
||||
| 编号 | 优先级 | 状态 | 模块 | 问题 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| R-001 | P0 | 已修复 | 服务端 | 公网接口无鉴权且允许任意来源 |
|
||||
| R-002 | P0 | 部分修复 | 全项目 | 明文密钥和凭据已进入项目文件 |
|
||||
| R-003 | P1 | 已修复 | 服务端 | `face_id` 可造成路径穿越写读删文件 |
|
||||
| R-004 | P1 | 已修复 | 服务端 | 大请求体会被静默截断 |
|
||||
| R-005 | P1 | 已修复 | 服务端 / Qt | CSV/JSON 没有转义会破坏数据和响应 |
|
||||
| R-006 | P1 | 已修复 | STM32 固件 | `sscanf` 未限制宽度,UART 输入可溢出栈缓冲 |
|
||||
| R-007 | P2 | 已修复 | 服务端 | 多个 `std::stoi` 可被非法参数打崩服务线程 |
|
||||
| R-008 | P2 | 已修复 | Qt 管理端 | 删除人脸 URL 未编码 |
|
||||
|
||||
## 2. 详细问题
|
||||
|
||||
### R-001 公网接口无鉴权且允许任意来源
|
||||
|
||||
- 优先级:P0
|
||||
- 状态:已修复
|
||||
- 位置:`server/server_encrypted.cpp:1666`
|
||||
- 影响:服务端暴露清空记录、删除人脸、上传映射、上传照片等写操作,但没有鉴权;响应固定允许任意跨域来源。只要端口可访问,外部请求就可能改写或删除考勤数据。
|
||||
- 建议处理:
|
||||
1. 给所有写接口增加 API Token 或签名校验。
|
||||
2. 按部署环境限制 CORS 来源,不再使用通配符。
|
||||
3. 公网部署时使用 HTTPS 和反向代理鉴权。
|
||||
4. 将管理类接口与设备上传接口区分权限。
|
||||
- 本次处理:主服务端 `server/server_encrypted.cpp` 已增加 `X-Api-Token` 校验,健康检查和 OPTIONS 预检除外;CORS 来源改为 `ATTENDANCE_CORS_ORIGIN` 配置,默认仅本地来源。Qt、STM32、K210 请求均已补 `X-Api-Token`。
|
||||
|
||||
### R-002 明文密钥和凭据已进入项目文件
|
||||
|
||||
- 优先级:P0
|
||||
- 状态:部分修复
|
||||
- 位置:`k210/main.py:34`、`User/main.c:23`、`QT/qml/Main.qml:2725`、`server/attendance.log`
|
||||
- 影响:项目文件中包含 WiFi 密码、公网服务地址、固定 AES Key/IV、查看账号密码、日志中的疑似 API Key 等敏感信息。泄露后即使代码修复,旧凭据仍可能被继续使用。
|
||||
- 建议处理:
|
||||
1. 立即轮换已暴露的 WiFi 密码、API Key、演示账号密码和 AES Key/IV。
|
||||
2. 将服务地址、WiFi、Token 等改为外部配置文件或烧录参数。
|
||||
3. 日志输出中屏蔽敏感请求头。
|
||||
4. 清理已有日志文件中的敏感内容。
|
||||
- 本次处理:主链路代码和文档中的 WiFi 密码、公网地址改为占位配置;日志中的疑似 API Key 已脱敏;Qt 默认服务地址改为本机地址。仍需在真实环境中轮换已暴露的 WiFi、API Key、演示账号和 AES Key/IV。
|
||||
|
||||
### R-003 `face_id` 可造成路径穿越写读删文件
|
||||
|
||||
- 优先级:P1
|
||||
- 状态:已修复
|
||||
- 位置:`server/server_encrypted.cpp:837`
|
||||
- 影响:`face_id` 直接来自 URL 并拼接成 `faces/<face_id>.jpg`,用于写入、读取和删除。包含路径分隔符或 `..` 时,可能越过 `faces` 目录操作其他文件。
|
||||
- 建议处理:
|
||||
1. 增加 `face_id` 白名单校验,只允许数字、字母、下划线、短横线等明确字符。
|
||||
2. 对最终路径做规范化检查,确认仍位于 `faces` 目录下。
|
||||
3. 所有照片读写删入口共用同一个安全路径构造函数。
|
||||
- 本次处理:主服务端已增加 `isSafeId` 与 `buildFacePhotoPath`,照片读写删和重命名路径统一走安全 ID 校验。
|
||||
|
||||
### R-004 大请求体会被静默截断
|
||||
|
||||
- 优先级:P1
|
||||
- 状态:已修复
|
||||
- 位置:`server/server_encrypted.cpp:1228`
|
||||
- 影响:读取 POST body 时,如果剩余数据超过约 40KB,循环不会继续读取,导致照片或配置内容被截断。K210 JPEG 上传很容易触发该问题。
|
||||
- 建议处理:
|
||||
1. 按 `Content-Length` 循环读取完整 body。
|
||||
2. 设置明确最大请求大小,例如照片接口单独允许更大上限。
|
||||
3. 超限时返回 HTTP 413。
|
||||
4. 对读取不足的请求返回 400,不保存半截文件。
|
||||
- 本次处理:主服务端按 `Content-Length` 读取完整请求体,普通 JSON 请求限制 64KB,照片请求限制 512KB,超限返回 413,读取不足返回 400。
|
||||
|
||||
### R-005 CSV/JSON 没有转义会破坏数据和响应
|
||||
|
||||
- 优先级:P1
|
||||
- 状态:已修复
|
||||
- 位置:`server/server_encrypted.cpp:459`、`QT/src/AppController.cpp:284`、`QT/src/AppController.cpp:317`
|
||||
- 影响:签到记录直接用逗号拼 CSV,读取时也直接按逗号拆,再手写 JSON 字符串。姓名、状态、班级等字段包含逗号、引号、换行或反斜杠时,会导致 CSV 列错位、JSON 非法或 Qt 解析失败。
|
||||
- 建议处理:
|
||||
1. 服务端增加 CSV 字段转义和解析函数。
|
||||
2. 服务端 JSON 响应统一经过 JSON 转义函数。
|
||||
3. Qt 导出 CSV 时对每个字段做标准 CSV 转义。
|
||||
4. 使用 SQLite 替代 CSV/多 JSON 文件,减少数据状态不一致。
|
||||
- 本次处理:主服务端增加 CSV 解析/转义和 JSON 转义;Qt 导出 CSV 已对字段做标准转义;后端主存储已迁移到 `attendance.db`,旧 CSV/JSON 仅作为首次导入来源。
|
||||
|
||||
### R-006 `sscanf` 未限制宽度,UART 输入可溢出栈缓冲
|
||||
|
||||
- 优先级:P1
|
||||
- 状态:已修复
|
||||
- 位置:`Hardware/K210_USART.c:80`
|
||||
- 影响:`%[^,]` 没有宽度限制,但目标缓冲只有 `id[20]` 和 `name[32]`。串口收到异常长字段时可能覆盖栈内存,造成固件异常。
|
||||
- 建议处理:
|
||||
1. 将格式改为 `%19[^,]`、`%31[^,]`。
|
||||
2. 对接收缓冲做最大帧长、换行结束符和字段合法性校验。
|
||||
3. 异常帧直接丢弃并清空缓冲。
|
||||
- 本次处理:`Hardware/K210_USART.c` 和示例文件中的 `sscanf` 已增加字段宽度限制。
|
||||
|
||||
### R-007 多个 `std::stoi` 可被非法参数打崩服务线程
|
||||
|
||||
- 优先级:P2
|
||||
- 状态:已修复
|
||||
- 位置:`server/server_encrypted.cpp:1310`
|
||||
- 影响:`limit`、`offset`、时间段参数直接调用 `std::stoi`,没有异常捕获和范围校验。非法请求参数可能让请求处理线程异常终止。
|
||||
- 建议处理:
|
||||
1. 增加安全整数解析函数。
|
||||
2. 参数非法时返回 400。
|
||||
3. 限制 `limit`、`offset` 和时间范围。
|
||||
4. 所有 URL 参数统一走校验函数。
|
||||
- 本次处理:主服务端已增加安全整数和时间解析,`limit`、`offset`、时间段和记录时间解析不再直接裸用 `std::stoi`。
|
||||
|
||||
### R-008 删除人脸 URL 未编码
|
||||
|
||||
- 优先级:P2
|
||||
- 状态:已修复
|
||||
- 位置:`QT/src/ApiClient.cpp:453`
|
||||
- 影响:Qt 端删除人脸时直接把 `studentId` 拼进路径。如果学号包含空格、`#`、`?`、`/` 或中文,URL 会被截断或路由错乱。
|
||||
- 建议处理:
|
||||
1. 使用 `QUrl::toPercentEncoding` 编码路径段。
|
||||
2. 服务端接收后进行 URL 解码和 ID 校验。
|
||||
3. 为删除人脸接口补充异常 ID 的测试用例。
|
||||
- 本次处理:Qt 删除人脸路径段已使用 `QUrl::toPercentEncoding` 编码。
|
||||
|
||||
## 3. 建议修复顺序
|
||||
|
||||
1. 处理 R-002:轮换凭据,清理敏感日志,避免继续泄露。
|
||||
2. 处理 R-001:服务端写接口增加鉴权,限制跨域来源。
|
||||
3. 处理 R-003、R-004:修复文件路径安全和请求体读取,防止文件被覆盖或照片损坏。
|
||||
4. 处理 R-006:修复固件串口解析溢出风险。
|
||||
5. 处理 R-005:统一 CSV/JSON 转义,避免数据格式破坏。
|
||||
6. 处理 R-007、R-008:增强参数健壮性和 Qt URL 构造。
|
||||
|
||||
## 4. 验证记录
|
||||
|
||||
- `k210/main.py`、`k210/view_attendance.py` 已通过普通 Python 语法检查;MaixPy 运行时仍需上板验证。
|
||||
- `server/server_encrypted.cpp` 因当前本机缺少 OpenSSL 头文件,未完成语法编译验证。
|
||||
- 现有 Keil 构建日志显示固件旧构建为 `0 Error(s), 0 Warning(s)`,但尚未在本次审查中重新编译固件。
|
||||
- 2026-05-01:已完成主链路安全修复;旧版备用服务端与测试脚本已清理,避免误部署非鉴权接口。
|
||||
@@ -0,0 +1,329 @@
|
||||
# 后端接口 Windows 手动测试命令
|
||||
|
||||
当前后端 `server_encrypted.cpp` 只监听 HTTP 端口:
|
||||
|
||||
```text
|
||||
8082
|
||||
```
|
||||
|
||||
以下命令适合在 Windows PowerShell 或 CMD 中复制执行。Windows 下建议使用 `curl.exe`,避免 PowerShell 把 `curl` 解析成别名。
|
||||
|
||||
如果后端在本机运行,使用:
|
||||
|
||||
```text
|
||||
http://127.0.0.1:8082
|
||||
```
|
||||
|
||||
如果测试公网服务器,替换成:
|
||||
|
||||
```text
|
||||
http://27.106.113.205:8082
|
||||
```
|
||||
|
||||
下面示例默认使用本机地址。
|
||||
|
||||
## 1. 测试端口和服务健康
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 "http://27.106.113.205:8082/api/v1/system/health"
|
||||
```
|
||||
|
||||
预期能看到 `HTTP/1.1 200 OK` 或类似成功响应。
|
||||
|
||||
## 2. 获取服务器时间
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://27.106.113.205:8082/api/v1/system/time"
|
||||
```
|
||||
|
||||
## 2.0 查看 SQLite 存储状态
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://27.106.113.205:8082/api/v1/system/storage"
|
||||
```
|
||||
|
||||
重点看:
|
||||
|
||||
```text
|
||||
"storage":"sqlite"
|
||||
"database":"attendance.db"
|
||||
"ok":true
|
||||
"quick_check":"ok"
|
||||
"records": 考勤记录数量
|
||||
"face_photos": 服务端照片数量
|
||||
"pending_deletes": K210待删除任务数量
|
||||
```
|
||||
|
||||
## 2.0.1 查看当前同步版本
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://27.106.113.205:8082/api/v1/sync/version"
|
||||
```
|
||||
|
||||
## 2.1 统一同步状态:K210
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://27.106.113.205:8082/api/v1/sync/state?client=k210&last_version=0"
|
||||
```
|
||||
|
||||
重点看:
|
||||
|
||||
```text
|
||||
"version": 当前服务端同步版本
|
||||
"changed": true/false
|
||||
"face_mappings": 人脸ID到学号映射
|
||||
"pending_delete": K210需要删除的本地人脸ID
|
||||
```
|
||||
|
||||
## 2.2 统一同步状态:STM32
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://27.106.113.205:8082/api/v1/sync/state?client=stm32&last_version=0"
|
||||
```
|
||||
|
||||
重点看 `time_slots` 是否正确返回。
|
||||
|
||||
## 2.3 统一同步状态:Qt
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://27.106.113.205:8082/api/v1/sync/state?client=qt&last_version=0"
|
||||
```
|
||||
|
||||
## 2.4 K210 确认统一同步删除
|
||||
|
||||
把 `2` 换成真实已删除成功的人脸 ID。
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" -H "Content-Type: application/json" -X POST --data-binary "{\"client\":\"k210\",\"version\":1,\"deleted\":[\"2\"]}" "http://27.106.113.205:8082/api/v1/sync/ack"
|
||||
```
|
||||
|
||||
## 3. 获取全部考勤记录
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://27.106.113.205:8082/api/v1/attendance/records?limit=1000&offset=0"
|
||||
```
|
||||
|
||||
后端当前以 `attendance.db` 为准。旧 `attendance_database.csv` 只会在首次启动 SQLite 时作为迁移来源,之后新增记录不会再写回 CSV。
|
||||
|
||||
如果本机安装了 `sqlite3.exe`,也可以在服务端运行目录执行:
|
||||
|
||||
```powershell
|
||||
sqlite3.exe attendance.db "SELECT timestamp,student_id,name,confidence,status,class_id FROM records ORDER BY id DESC LIMIT 10;"
|
||||
```
|
||||
|
||||
备份当前数据库:
|
||||
|
||||
```powershell
|
||||
sqlite3.exe attendance.db ".backup attendance_backup.db"
|
||||
```
|
||||
|
||||
## 4. 获取统计信息
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://27.106.113.205:8082/api/v1/attendance/statistics"
|
||||
```
|
||||
|
||||
## 5. 获取签到时间段
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://27.106.113.205:8082/api/v1/config/time-slots"
|
||||
```
|
||||
|
||||
## 6. 保存签到时间段
|
||||
|
||||
用于测试 Qt 端 `/api/v1/config/time-slots` 保存失败的问题。执行后观察后端日志,正常应该显示 `POST body size` 大于 0。
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" -H "Content-Type: application/json" -X POST --data-binary "{\"time_slots\":[{\"name\":\"上午签到\",\"start_time\":\"08:00\",\"end_time\":\"09:30\",\"enabled\":true},{\"name\":\"中午签退\",\"start_time\":\"12:00\",\"end_time\":\"13:00\",\"enabled\":true},{\"name\":\"下午签到\",\"start_time\":\"14:00\",\"end_time\":\"15:00\",\"enabled\":true},{\"name\":\"晚上签退\",\"start_time\":\"17:00\",\"end_time\":\"18:30\",\"enabled\":true}]}" "http://27.106.113.205:8082/api/v1/config/time-slots"
|
||||
```
|
||||
|
||||
## 7. 检查是否重复签到
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://27.106.113.205:8082/api/v1/attendance/duplicates?student_id=1&slot_start=00:00&slot_end=23:59"
|
||||
```
|
||||
|
||||
## 8. 获取今天指定时间段记录
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://27.106.113.205:8082/api/v1/attendance/today?slot_start=00:00&slot_end=23:59"
|
||||
```
|
||||
|
||||
也可以测试指定签到时间段:
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://27.106.113.205:8082/api/v1/attendance/today?slot_start=14:00&slot_end=15:00"
|
||||
```
|
||||
|
||||
## 9. 获取映射配置
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://27.106.113.205:8082/api/v1/config/mappings"
|
||||
```
|
||||
|
||||
## 10. 保存映射配置
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" -H "Content-Type: application/json" -X POST --data-binary "{\"student_name_map\":{\"1\":\"ID1\"},\"class_name_map\":{\"1\":\"一班\"}}" "http://27.106.113.205:8082/api/v1/config/mappings"
|
||||
```
|
||||
|
||||
## 11. 获取人脸照片列表
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://27.106.113.205:8082/api/v1/faces/photos"
|
||||
```
|
||||
|
||||
## 12. 下载指定人脸照片
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" "http://27.106.113.205:8082/api/v1/faces/photos/1" --output "%TEMP%\backend_face_1.jpg"
|
||||
```
|
||||
|
||||
如果 `face_id=1` 存在,照片会保存到:
|
||||
|
||||
```text
|
||||
%TEMP%\backend_face_1.jpg
|
||||
```
|
||||
|
||||
## 13. 上传人脸照片
|
||||
|
||||
把 `C:\path\to\face.jpg` 换成真实 jpg 文件路径。
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Content-Type: image/jpeg" --data-binary "@C:\path\to\face.jpg" "http://27.106.113.205:8082/api/v1/faces/photos/1"
|
||||
```
|
||||
|
||||
## 14. 获取人脸 ID 映射
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://27.106.113.205:8082/api/v1/faces/mappings"
|
||||
```
|
||||
|
||||
## 15. 保存人脸 ID 映射
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" -H "Content-Type: application/json" -X POST --data-binary "{\"mappings\":{\"1\":\"1\"}}" "http://27.106.113.205:8082/api/v1/faces/mappings"
|
||||
```
|
||||
|
||||
## 16. 获取全部学生 ID
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://27.106.113.205:8082/api/v1/faces/student-ids"
|
||||
```
|
||||
|
||||
## 17. 获取待删除列表
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://27.106.113.205:8082/api/v1/faces/pending-delete"
|
||||
```
|
||||
|
||||
## 18. 确认删除同步完成
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" -H "Content-Type: application/json" -X POST --data-binary "{\"face_id\":\"1\"}" "http://27.106.113.205:8082/api/v1/faces/delete-ack"
|
||||
```
|
||||
|
||||
## 19. K210 查询是否需要同步
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://27.106.113.205:8082/api/v1/sync/request"
|
||||
```
|
||||
|
||||
## 20. 触发 K210 同步
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" -H "Content-Type: application/json" -X POST --data-binary "{\"sync\":true}" "http://27.106.113.205:8082/api/v1/sync/trigger"
|
||||
```
|
||||
|
||||
## 21. K210 同步确认
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" -H "Content-Type: application/json" -X POST --data-binary "{\"ack\":true}" "http://27.106.113.205:8082/api/v1/sync/request/ack"
|
||||
```
|
||||
|
||||
## 22. 加密签到接口
|
||||
|
||||
`/api/v1/attendance/checkins` 需要 STM32 生成的 AES-128-CBC Base64 加密正文。把 `BODY` 替换成真实加密数据。
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "X-Encrypted: AES128-CBC" -H "Content-Type: application/octet-stream" --data-binary "BODY" "http://27.106.113.205:8082/api/v1/attendance/checkins"
|
||||
```
|
||||
|
||||
## 23. 删除单条考勤记录
|
||||
|
||||
危险操作,会删除真实数据。执行前把时间和学生 ID 改成要删除的记录。
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -X DELETE "http://27.106.113.205:8082/api/v1/attendance/records?timestamp=2026-05-01%2014%3A30%3A38&student_id=1"
|
||||
```
|
||||
|
||||
## 24. 删除指定人脸
|
||||
|
||||
危险操作,只删除人脸信息,不删除考勤记录。
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -X DELETE "http://27.106.113.205:8082/api/v1/faces/1"
|
||||
```
|
||||
|
||||
## 25. 删除指定人脸并删除相关考勤记录
|
||||
|
||||
危险操作,会删除人脸和相关记录。
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -X DELETE "http://27.106.113.205:8082/api/v1/faces/1?delete_records=true"
|
||||
```
|
||||
|
||||
## 26. 清空全部考勤记录
|
||||
|
||||
危险操作,会清空考勤数据库。
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Content-Type: application/json" -X POST --data-binary "{}" "http://27.106.113.205:8082/api/v1/attendance/records/clear"
|
||||
```
|
||||
|
||||
## 27. Windows 上检查端口占用
|
||||
|
||||
```powershell
|
||||
netstat -ano | findstr :8082
|
||||
```
|
||||
|
||||
如果能看到 `LISTENING`,说明本机有程序正在监听 `8082`。
|
||||
|
||||
## 28. Windows 上测试 TCP 端口连通
|
||||
|
||||
```powershell
|
||||
Test-NetConnection 127.0.0.1 -Port 8082
|
||||
```
|
||||
|
||||
公网服务器测试:
|
||||
|
||||
```powershell
|
||||
Test-NetConnection 27.106.113.205 -Port 8082
|
||||
```
|
||||
|
||||
`TcpTestSucceeded : True` 表示 TCP 端口可连通。
|
||||
|
||||
## 29. 判断 Qt POST 是否真的发送了请求体
|
||||
|
||||
先手动执行这条:
|
||||
|
||||
```powershell
|
||||
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" -H "Content-Type: application/json" -X POST --data-binary "{\"time_slots\":[{\"name\":\"下午签到\",\"start_time\":\"14:00\",\"end_time\":\"15:00\",\"enabled\":true}]}" "http://27.106.113.205:8082/api/v1/config/time-slots"
|
||||
```
|
||||
|
||||
如果后端日志显示:
|
||||
|
||||
```text
|
||||
[HTTP] POST body size: xx bytes
|
||||
```
|
||||
|
||||
说明服务端可以正常接收请求体。
|
||||
|
||||
如果 Qt 保存时后端仍显示:
|
||||
|
||||
```text
|
||||
[HTTP] POST body size: 0 bytes
|
||||
```
|
||||
|
||||
说明问题在 Qt 端发送 POST 的方式上。
|
||||
|
||||
@@ -0,0 +1,272 @@
|
||||
# 后续优化路线
|
||||
|
||||
本文档记录当前项目在“可继续优化但非当前阻塞”的方向,方便后续开发、答辩扩展或二次重构时逐项推进。
|
||||
|
||||
## 1. 当前架构状态
|
||||
|
||||
当前项目已经完成以下核心升级:
|
||||
|
||||
- API 已统一到 `/api/v1/...`,旧 `/api/...` 仅作为服务端内部兼容层。
|
||||
- 服务端使用 `attendance.db` 作为主存储,旧 CSV/JSON 文件仅作为首次迁移来源。
|
||||
- 服务端维护全局同步版本,Qt、STM32、K210 按 `last_version` 增量同步。
|
||||
- K210 删除人脸使用待删除任务和 ACK 确认。
|
||||
- K210 新增人脸照片先保存到本地 `/sd/face_photos/`,再补传服务端。
|
||||
- Qt 端照片由 `ApiClient` 携带鉴权头下载并缓存为本地 `file://` 地址显示。
|
||||
- 服务端提供 `/api/v1/system/storage` 和 `/api/v1/sync/version` 用于调试存储和同步状态。
|
||||
|
||||
结论:当前架构已经适合联调、演示和毕业设计答辩。后续优化以工程质量、稳定性和可维护性为主,不建议在演示前集中大改。
|
||||
|
||||
## 2. 优化优先级
|
||||
|
||||
| 优先级 | 方向 | 当前是否建议立即做 | 主要收益 |
|
||||
| --- | --- | --- | --- |
|
||||
| P1 | 服务端 JSON 解析换成 `nlohmann/json` | 暂缓 | 降低手写解析风险,提升维护性 |
|
||||
| P1 | 数据库自动备份与恢复文档 | 可做 | 防止误删或调试破坏数据 |
|
||||
| P2 | 同步版本按模块拆分 | 暂缓 | 降低无关数据重复同步 |
|
||||
| P2 | 删除任务 task_id 化 | 暂缓 | 删除同步更严谨,避免 ID 歧义 |
|
||||
| P2 | 设备同步 ACK 状态表 | 暂缓 | 可追踪每台设备同步进度 |
|
||||
| P2 | 外部配置文件/环境变量统一管理 | 可做 | 降低硬编码凭据风险 |
|
||||
| P3 | HTTPS/反向代理部署 | 视部署环境 | 公网安全性更好 |
|
||||
| P3 | 自动化测试脚本 | 可做 | 回归验证更方便 |
|
||||
| P3 | Qt 同步状态面板 | 可做 | 调试体验更好 |
|
||||
|
||||
## 3. 服务端 JSON 库化
|
||||
|
||||
当前状态:
|
||||
|
||||
- 服务端部分请求解析和响应构造仍使用字符串查找和手写 JSON 拼接。
|
||||
- 已有 `jsonEscape()`,可以避免大多数响应字符串破坏。
|
||||
- SQLite 已解决 CSV/多 JSON 文件存储不一致问题。
|
||||
|
||||
建议方案:
|
||||
|
||||
- 引入 `nlohmann/json` 单头文件版本。
|
||||
- 文件建议放到:
|
||||
|
||||
```text
|
||||
server/third_party/json.hpp
|
||||
```
|
||||
|
||||
- 服务端引用方式:
|
||||
|
||||
```cpp
|
||||
#include "third_party/json.hpp"
|
||||
using json = nlohmann::json;
|
||||
```
|
||||
|
||||
建议分阶段替换:
|
||||
|
||||
1. 替换签到请求体解析:`/api/v1/attendance/checkins`
|
||||
2. 替换时间段保存校验:`/api/v1/config/time-slots`
|
||||
3. 替换人脸映射保存:`/api/v1/faces/mappings`
|
||||
4. 替换同步 ACK 解析:`/api/v1/sync/ack`
|
||||
5. 最后替换大型响应构造:`/api/v1/sync/state`
|
||||
|
||||
注意事项:
|
||||
|
||||
- `nlohmann/json` 不需要额外链接库,只需要头文件。
|
||||
- 引入后编译命令通常不变。
|
||||
- 不建议一次性替换所有 JSON 逻辑,容易引入新问题。
|
||||
|
||||
## 4. 同步版本拆分
|
||||
|
||||
当前状态:
|
||||
|
||||
- 服务端维护一个全局同步版本。
|
||||
- 任何数据变化都会让 Qt、STM32、K210 看到版本变化。
|
||||
- 对当前规模足够清晰,也方便调试。
|
||||
|
||||
后续可升级为:
|
||||
|
||||
```text
|
||||
records_version
|
||||
config_version
|
||||
faces_version
|
||||
delete_tasks_version
|
||||
```
|
||||
|
||||
收益:
|
||||
|
||||
- STM32 只关心 `config_version`。
|
||||
- K210 只关心 `faces_version` 和 `delete_tasks_version`。
|
||||
- Qt 可按模块决定是否刷新记录、配置、照片。
|
||||
|
||||
代价:
|
||||
|
||||
- 服务端同步响应结构要调整。
|
||||
- Qt、STM32、K210 都要保存多个版本号。
|
||||
- 调试复杂度会上升。
|
||||
|
||||
建议:当前不急,除非后续记录量明显变大或同步频率变高。
|
||||
|
||||
## 5. 删除任务 task_id 化
|
||||
|
||||
当前状态:
|
||||
|
||||
- 待删除任务以 `face_id` 列表形式保存。
|
||||
- K210 删除成功后通过 ACK 返回已删除 ID。
|
||||
|
||||
后续可升级为:
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "delete_20260501_0001",
|
||||
"face_id": "1",
|
||||
"student_id": "20210001",
|
||||
"created_at": "2026-05-01 14:30:00",
|
||||
"status": "pending"
|
||||
}
|
||||
```
|
||||
|
||||
收益:
|
||||
|
||||
- 删除任务可追踪。
|
||||
- 重复删除、重新添加同 ID 时更不容易混淆。
|
||||
- 可以在 Qt 端显示待删除任务状态。
|
||||
|
||||
建议表结构:
|
||||
|
||||
```sql
|
||||
CREATE TABLE delete_tasks (
|
||||
task_id TEXT PRIMARY KEY,
|
||||
face_id TEXT NOT NULL,
|
||||
student_id TEXT,
|
||||
status TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
ack_at TEXT
|
||||
);
|
||||
```
|
||||
|
||||
建议:如果后续要增强多设备同步审计,再做。
|
||||
|
||||
## 6. 设备同步状态表
|
||||
|
||||
当前状态:
|
||||
|
||||
- 各设备本地保存自己的 `last_version`。
|
||||
- 服务端不记录每台设备最后同步到哪个版本。
|
||||
|
||||
后续可增加:
|
||||
|
||||
```sql
|
||||
CREATE TABLE device_sync_state (
|
||||
client TEXT PRIMARY KEY,
|
||||
last_seen_version INTEGER,
|
||||
last_ack_version INTEGER,
|
||||
last_seen_at TEXT
|
||||
);
|
||||
```
|
||||
|
||||
收益:
|
||||
|
||||
- 可以知道 K210、STM32、Qt 是否长期未同步。
|
||||
- 服务端可以在 `/api/v1/system/storage` 中显示设备同步状态。
|
||||
- 方便做 Qt 同步状态面板。
|
||||
|
||||
建议:当前不是必须,适合作为后续展示增强。
|
||||
|
||||
## 7. 数据库备份与恢复
|
||||
|
||||
当前手动备份命令:
|
||||
|
||||
```powershell
|
||||
sqlite3.exe attendance.db ".backup attendance_backup.db"
|
||||
```
|
||||
|
||||
后续可增加:
|
||||
|
||||
- 服务端启动时自动备份前一天数据库。
|
||||
- Qt 端提供“导出数据库备份”按钮。
|
||||
- 文档中增加恢复步骤。
|
||||
|
||||
恢复示例:
|
||||
|
||||
```powershell
|
||||
Copy-Item attendance_backup.db attendance.db
|
||||
```
|
||||
|
||||
注意:恢复前应先停止服务端,避免写入冲突。
|
||||
|
||||
## 8. 配置外部化
|
||||
|
||||
当前仍存在部分硬编码配置:
|
||||
|
||||
- K210 WiFi 名称和密码。
|
||||
- K210 服务端 IP 和端口。
|
||||
- STM32 服务端 IP、WiFi、班级 ID。
|
||||
- 默认 `X-Api-Token`。
|
||||
- AES Key/IV。
|
||||
|
||||
建议方向:
|
||||
|
||||
- 服务端继续使用环境变量管理:
|
||||
|
||||
```text
|
||||
ATTENDANCE_API_TOKEN
|
||||
ATTENDANCE_CORS_ORIGIN
|
||||
```
|
||||
|
||||
- K210 可使用 `/sd/config.txt` 保存 WiFi、服务端和 token。
|
||||
- STM32 可集中放在 `User/main.c` 顶部,并在文档中标记部署必改项。
|
||||
- Qt 可在设置页保存服务端地址,token 可后续加入设置项。
|
||||
|
||||
建议:真实公网部署前必须轮换默认 token、WiFi 密码和 AES Key/IV。
|
||||
|
||||
## 9. Qt 同步状态面板
|
||||
|
||||
后续可在 Qt 管理端增加一个调试/状态区域,展示:
|
||||
|
||||
- 服务器连接状态。
|
||||
- 当前服务端同步版本。
|
||||
- 最近同步时间。
|
||||
- `records` 数量。
|
||||
- `face_mappings` 数量。
|
||||
- `face_photos` 数量。
|
||||
- `pending_deletes` 数量。
|
||||
|
||||
数据来源:
|
||||
|
||||
```text
|
||||
GET /api/v1/system/storage
|
||||
GET /api/v1/sync/version
|
||||
```
|
||||
|
||||
收益:
|
||||
|
||||
- 联调时不用一直看服务端日志。
|
||||
- 演示时可以直观看到多端同步状态。
|
||||
|
||||
## 10. 自动化测试
|
||||
|
||||
当前已有手动 curl 文档:
|
||||
|
||||
```text
|
||||
docs/06_后端接口Windows测试命令.md
|
||||
```
|
||||
|
||||
后续可增加脚本化测试,但不要替代手动文档:
|
||||
|
||||
- Windows PowerShell 测试脚本。
|
||||
- Linux shell 测试脚本。
|
||||
- 服务端启动后自动测试 health、storage、records、time-slots、sync/state。
|
||||
|
||||
建议先保持手动命令文档为主,因为硬件联调环境经常变化。
|
||||
|
||||
## 11. 暂不建议立刻做的优化
|
||||
|
||||
以下方向有价值,但当前阶段不建议马上做:
|
||||
|
||||
- 删除旧 `/api/...` 兼容层:会降低调试容错,建议答辩后再考虑。
|
||||
- 全量重写同步协议:当前全局版本方案已经足够。
|
||||
- 把照片二进制直接存 SQLite:会让数据库变大,当前文件系统存照片、SQLite 存元数据更合适。
|
||||
- 一次性替换所有 JSON 逻辑:风险高,建议分阶段。
|
||||
- 引入复杂权限系统:当前 `X-Api-Token` 已满足演示需求,公网正式部署再细分权限。
|
||||
|
||||
## 12. 推荐后续开发顺序
|
||||
|
||||
1. 先完成 SQLite 版服务端手动编译和接口验证。
|
||||
2. 用 `/api/v1/system/storage` 验证记录、映射、照片、待删除任务数量。
|
||||
3. 如需继续提升服务端质量,先引入 `nlohmann/json` 替换请求解析。
|
||||
4. 如需继续提升演示体验,再做 Qt 同步状态面板。
|
||||
5. 如需继续提升同步严谨性,再做 task_id 删除任务和设备同步状态表。
|
||||
|
||||
Reference in New Issue
Block a user