硬件基本完成

This commit is contained in:
2026-05-01 21:34:10 +08:00
parent df5b05e936
commit 5a31ff2179
14 changed files with 1005 additions and 439 deletions
+1 -2
View File
@@ -12,7 +12,7 @@
1. 服务端:`server/server_encrypted.cpp`(HTTP 8082)
2. STM32:`User/main.c`(加密上传 + 时间段同步)
3. K210:`k210/main.py`(识别 + 照片上传 + 同步)
4. Qt:`QT/AttendanceViewer.pro` 构建的客户端
4. Qt:`QT/CMakeLists.txt` 构建的客户端
## 3. 总体架构
1. K210 识别人脸,经 UART 上报 STM32。
@@ -28,7 +28,6 @@
- 服务端:`docs/03_服务端文档.md`
- Qt 管理端:`docs/04_Qt管理端文档.md`
- 项目审查问题清单:`docs/05_项目审查问题清单.md`
- 后续优化路线:`docs/07_后续优化路线.md`
## 5. 各端关键配置位置
- STM32 网络与班级配置:`User/main.c`
+35 -5
View File
@@ -11,10 +11,13 @@
## 2. 端口与数据文件
- HTTP 端口:`8082`
- SQLite 数据库:`attendance.db`
- 启动备份目录:`backups/`
- 日志:运行时生成 `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` 表中。
- 删除任务:存储在 `pending_delete` 表中,包含 `task_id`、`face_id`、`student_id`、`created_at`、`status`、`ack_at`。
- 设备同步状态:存储在 `device_sync_state` 表中,用于追踪各端最近同步版本和 ACK 版本。
## 3. 签到数据链路
1. STM32 调用 `POST /api/v1/attendance/checkins` 上传 Base64 密文。
@@ -60,6 +63,7 @@
- 服务端是唯一权威状态源。
- 每次签到、清空/删除记录、修改时间段、修改映射、上传照片、删除人脸,服务端都会递增 `kv_store.sync_version`。
- 设备端保存自己的 `last_version`,请求时带给服务端。
- 服务端会记录每个 `client` 的 `last_seen_version`、`last_ack_version` 和 `last_seen_at`,可通过 `/api/v1/system/storage` 查看。
- 如果版本无变化,服务端返回 `changed:false`,设备端无需重复解析配置。
- 如果版本有变化,服务端返回 `changed:true` 和当前客户端需要的状态。
- `/api/v1/sync/version` 只返回当前版本和服务端时间,适合调试时快速确认同步版本。
@@ -76,7 +80,8 @@ K210 删除确认示例:
{
"client": "k210",
"version": 12,
"deleted": ["2", "3"]
"deleted": ["2", "3"],
"task_ids": ["2-1770000000", "3-1770000001"]
}
```
@@ -110,13 +115,38 @@ g++ -o server_encrypted server_encrypted.cpp -lcrypto -lssl -lsqlite3 -lpthread
说明:服务端启动时会自动创建 `attendance.db`。如果检测到旧 CSV/JSON 文件,会在首次启动时导入;之后以后端 API 和 SQLite 为准。
## 6. 版本说明
## 6. 备份与恢复
服务端启动时如果发现已有 `attendance.db`,会先复制到 `backups/attendance_YYYY-MM-DD_HH-MM-SS.db`。备份失败时服务端会停止启动,避免在没有恢复点的情况下继续写入。
恢复步骤:
1. 停止 `server_encrypted`。
2. 将当前 `attendance.db` 改名保留,例如 `attendance.broken.db`。
3. 从 `backups/` 选择最近可用备份,复制为 `attendance.db`。
4. 重新启动服务端,访问 `/api/v1/system/storage`,确认 `ok:true`、`quick_check:"ok"`。
## 7. 公网部署安全
本机或内网演示可以直接监听 `0.0.0.0:8082`。公网部署建议:
- 设置 `ATTENDANCE_BIND_HOST=127.0.0.1`,只让服务端监听本机。
- 使用 Nginx 或 Caddy 终止 HTTPS,并由反向代理转发到 `127.0.0.1:8082`。
- 配置 HTTPS 后立即轮换 `ATTENDANCE_API_TOKEN`、设备端 Token 和相关密钥。
- 反向代理层继续限制来源 IP、请求体大小和访问日志脱敏。
## 8. 自动化回归
服务端启动后可运行:
```powershell
powershell -ExecutionPolicy Bypass -File .\regression_api_tests.ps1 -BaseUrl "http://127.0.0.1:8082" -ApiToken "change-me-token"
```
脚本覆盖 health、storage、time、sync、records、statistics、time-slots、mappings、faces 等低风险接口;真实 AES 签到正文和真实照片上传仍保留手动验证。
## 9. 版本说明
仓库仅保留当前推荐版本:`server_encrypted.cpp`。旧版实验服务端与测试脚本已清理,避免误部署非鉴权接口。
## 7. 关键一致性要求
## 10. 关键一致性要求
- AES Key/IV 必须与 STM32、Qt 完全一致。
- 若 Qt 无法解析记录,先检查是否误连到非加密版本服务端。
## 8. 后续优化
服务端当前已经完成 SQLite 主存储、v1 接口和统一同步模型。后续如需继续增强 JSON 解析、同步版本拆分、删除任务 task_id 化、数据库备份或 Qt 同步状态面板,参考 `docs/07_后续优化路线.md`。
## 11. 后续优化
服务端当前已经完成 SQLite 主存储、v1 接口、自动备份和统一同步模型。后续需要继续跟踪的风险和优化项已统一记录在 `docs/05_项目审查问题清单.md`。
+209 -7
View File
@@ -21,6 +21,20 @@
| R-006 | P1 | 已修复 | STM32 固件 | `sscanf` 未限制宽度,UART 输入可溢出栈缓冲 |
| R-007 | P2 | 已修复 | 服务端 | 多个 `std::stoi` 可被非法参数打崩服务线程 |
| R-008 | P2 | 已修复 | Qt 管理端 | 删除人脸 URL 未编码 |
| R-009 | P1 | 已修复 | STM32 固件 | NTP-only 模式下时间不会前进 |
| R-010 | P1 | 已修复 | STM32 固件 | 发送成功判断过早,可能静默丢签到 |
| R-011 | P1 | 已修复 | STM32 固件 | 今日缓存不区分时段,会误判重复签到 |
| R-012 | P1 | 已修复 | 服务端 | 签到请求手写 JSON 解析会被特殊字符破坏 |
| R-013 | P2 | 已修复 | K210 识别端 | 从 SD 卡加载特征时使用 `eval` |
| R-014 | P2 | 已修复 | 文档 | 总文档指向不存在的 Qt 工程文件 |
| R-015 | P2 | 已修复 | 服务端 / 数据 | 数据库缺少自动备份与恢复流程 |
| R-016 | P2 | 已修复 | 服务端 / K210 | 删除任务缺少 task_id 和状态跟踪 |
| R-017 | P2 | 已修复 | 服务端 / 多端同步 | 设备同步状态不可追踪 |
| R-018 | P2 | 已修复 | 部署 / 安全 | 公网 HTTP 明文传输缺少 HTTPS 或反向代理保护 |
| R-019 | P3 | 已修复 | 测试 | 关键接口缺少自动化回归脚本 |
| R-020 | P1 | 已修复 | K210 / 同步 | 删除 ACK 失败后本地版本仍前进,可能卡住删除任务 |
| R-021 | P2 | 已修复 | STM32 固件 / 多设备 | 多设备或缓存过期时重复签到仍可能漏判 |
| R-022 | P2 | 已修复 | STM32 固件 / ESP8266 | HTTP GET 可能接受半截响应 |
## 2. 详细问题
@@ -48,7 +62,7 @@
2. 将服务地址、WiFi、Token 等改为外部配置文件或烧录参数。
3. 日志输出中屏蔽敏感请求头。
4. 清理已有日志文件中的敏感内容。
- 本次处理:主链路代码和文档中的 WiFi 密码、公网地址改为占位配置;日志中的疑似 API Key 已脱敏;Qt 默认服务地址改为本机地址。仍需在真实环境中轮换已暴露的 WiFi、API Key、演示账号和 AES Key/IV。
- 本次处理:主链路代码和文档中的 WiFi 密码、公网地址改为占位配置;Qt 关于页移除固定演示账号密码;日志中的疑似 API Key 已脱敏;Qt 默认服务地址改为本机地址。仍需在真实环境中轮换已暴露的 WiFi、API Key、演示账号和 AES Key/IV。
### R-003 `face_id` 可造成路径穿越写读删文件
@@ -125,14 +139,197 @@
3. 为删除人脸接口补充异常 ID 的测试用例。
- 本次处理:Qt 删除人脸路径段已使用 `QUrl::toPercentEncoding` 编码。
### R-009 NTP-only 模式下时间不会前进
- 优先级:P1
- 状态:已修复
- 位置:`User/main.c:467-472`
- 影响:DS1302 不可用时,系统每秒只是把 `g_current_time.hour/minute` 再写入考勤模块,没有按系统 tick 推进秒、分钟和日期。同步一次 NTP 后,考勤时间会冻结在同步瞬间,后续时段判断可能长期错误。
- 建议处理:
1. 在 NTP-only 模式下基于 `Get_Time_ms()` 维护软件时钟。
2. 每秒递增 `g_current_time`,正确处理分钟、小时、日期进位。
3. NTP 同步成功时重置软件时钟基准。
4. 增加跨分钟、跨小时、跨天的基本验证。
- 本次处理:`User/main.c` 增加软件时钟推进逻辑,DS1302 不可用时按 `Get_Time_ms()` 每秒递增秒、分钟、小时和日期;NTP 同步成功后重置推进基准。
### R-010 发送成功判断过早,可能静默丢签到
- 优先级:P1
- 状态:已修复
- 位置:`Hardware/esp8266.c:230-236`
- 影响:STM32 侧 POST 只等待 ESP8266 的 `>` 提示并写出 HTTP 字符串,随后立即返回成功;没有等待 `SEND OK`,也没有检查服务端 HTTP 200 或 JSON `success`。网络中断、服务端 401/500、请求体未落库时,调用方仍可能把记录标记为已同步,造成静默丢签到。
- 建议处理:
1. 发送后等待 `SEND OK`,失败时返回 `ESP8266_FAIL`。
2. 继续读取 HTTP 响应,确认状态码为 200 且响应体包含成功标志。
3. 超时、401、500、响应解析失败都应保留记录为待同步或失败待重试。
4. 记录服务端返回的错误信息,便于联调排查 Token、加密和数据库问题。
- 本次处理:`Hardware/esp8266.c` 的加密 POST 发送后会等待 `SEND OK`、HTTP 200 和响应体 `success:true`;超时、发送失败、401/400/413/500 或 `success:false` 都返回失败,使记录保留为待重试。
### R-011 今日缓存不区分时段,会误判重复签到
- 优先级:P1
- 状态:已修复
- 位置:`Hardware/Attendance.c:75-108`
- 影响:当前缓存只保存 `student_id`,不保存考勤时段。任一时段签到成功后都会进入缓存,后续中午、下午、晚上时段再签到可能被 `Attendance_IsDuplicateInCache` 判为重复。开机不在时段时还会按 `00:00-23:59` 拉取全天记录,误判范围更大。
- 建议处理:
1. 缓存项增加时段编号或 `slot_start/slot_end` 字段。
2. 本地新增签到写缓存时同时写入当前时段。
3. 服务器下载今日记录时只填充当前有效时段的缓存;不在时段内不要拉取全天缓存用于查重。
4. 切换时段时清理或重建缓存,避免跨时段污染。
- 本次处理:`Hardware/Attendance.c` 的缓存项增加时段绑定;新增签到和服务器下载缓存都写入当前时段;不在时段内不再拉取全天缓存;时段切换时自动清空缓存。
### R-012 签到请求手写 JSON 解析会被特殊字符破坏
- 优先级:P1
- 状态:已修复
- 位置:`server/server_encrypted.cpp:707-742`
- 影响:`parseJSON` 直接按逗号拆字段、按第一个冒号拆键值,无法正确处理字符串里的逗号、冒号、反斜杠和转义引号。签到姓名、状态或后续字段一旦包含这些字符,会解析错列甚至写入错误数据。
- 建议处理:
1. 引入 `nlohmann/json` 等成熟 JSON 库,优先替换签到请求体解析。
2. 对必填字段做类型、长度和取值校验。
3. JSON 解析失败时返回 400,避免写入半解析数据。
4. 增加包含逗号、冒号、引号和反斜杠的解析测试样例。
- 本次处理:`server/server_encrypted.cpp` 的签到 JSON 解析改为逐字符解析字符串字面量,支持逗号、冒号、反斜杠和转义引号;签到字段增加 ID、长度、置信度范围和状态取值校验,非法请求返回 400。
### R-013 K210 从 SD 卡加载特征时使用 `eval`
- 优先级:P2
- 状态:已修复
- 位置:`k210/main.py:1315-1321`
- 影响:`recordftr.txt` 内容来自可写 SD 卡,`eval(parts[1])` 会执行任意 Python 表达式。文件损坏或被替换时,可能执行意外代码、卡死或耗尽内存。
- 建议处理:
1. 如果 MaixPy 支持,改用 `ast.literal_eval`。
2. 如果不支持,改为明确的浮点数组解析器,只接受数字、逗号、方括号和空白。
3. 校验特征向量长度和数值范围,异常记录跳过并打印错误。
4. 保存特征时考虑使用更容易校验的 JSON 或二进制格式。
- 本次处理:`k210/main.py` 已用 `parse_feature_vector` 替换 `eval`,只接受浮点数组允许字符,并校验向量长度,异常记录会被跳过并打印解析错误。
### R-014 总文档指向不存在的 Qt 工程文件
- 优先级:P2
- 状态:已修复
- 位置:`docs/00_项目总文档.md:15`
- 影响:总文档写的是用 `QT/AttendanceViewer.pro` 构建,但仓库里没有该文件;实际 Qt 项目使用 `QT/CMakeLists.txt`。部署、答辩复现或新成员接手时会被误导。
- 建议处理:
1. 将总文档中的 Qt 构建入口改为 `QT/CMakeLists.txt`。
2. 与 `docs/04_Qt管理端文档.md` 保持一致,说明当前推荐 CMake 构建。
3. 全仓库搜索旧 `.pro`、`qmake` 和旧接口路径,清理过时说明。
- 本次处理:总文档中的 Qt 构建入口已改为 `QT/CMakeLists.txt`;删除 `docs/07_后续优化路线.md` 前同步清理了相关引用。
### R-015 数据库缺少自动备份与恢复流程
- 优先级:P2
- 状态:已修复
- 位置:`server/server_encrypted.cpp`、`docs/03_服务端文档.md`
- 影响:当前服务端使用 `attendance.db` 作为主存储,但没有自动备份、恢复步骤和误删后的应急流程。调试、答辩演示或清空记录接口误操作时,可能导致考勤记录、映射和同步状态难以恢复。
- 建议处理:
1. 增加服务端启动前或每日首次启动的 SQLite 备份逻辑。
2. 在服务端文档中补充停止服务、备份、恢复和校验步骤。
3. Qt 端后续可增加导出数据库备份入口。
4. 备份文件应带日期时间后缀,避免覆盖最近可用备份。
- 本次处理:服务端启动时会把已有 `attendance.db` 复制到 `backups/attendance_YYYY-MM-DD_HH-MM-SS.db`,备份失败则停止启动;`docs/03_服务端文档.md` 已补恢复和校验流程。
### R-016 删除任务缺少 task_id 和状态跟踪
- 优先级:P2
- 状态:已修复
- 位置:`server/server_encrypted.cpp`、`k210/main.py`
- 影响:当前待删除任务主要按 `face_id` 列表流转,K210 删除成功后按 ID ACK。重复删除、删除后重新添加同 ID、多设备并发同步时,可能难以区分旧任务和新任务,也不方便追踪任务创建、下发、确认和失败状态。
- 建议处理:
1. 将删除任务升级为带 `task_id`、`face_id`、`student_id`、`created_at`、`status`、`ack_at` 的记录。
2. K210 ACK 时回传 `task_id`,服务端按任务确认。
3. 保留兼容旧 `face_id` ACK 的过渡逻辑,避免一次性破坏现有联调链路。
4. 在 Qt 或服务端状态接口中暴露待删除任务数量和最近任务状态。
- 本次处理:服务端 `pending_delete` 升级为任务表,包含 `task_id`、`face_id`、`student_id`、`created_at`、`status`、`ack_at`;同步响应同时返回兼容旧数组和 `delete_tasks`;K210 ACK 会回传 `task_ids`,服务端仍兼容旧 `face_id` 确认。
### R-017 设备同步状态不可追踪
- 优先级:P2
- 状态:已修复
- 位置:`server/server_encrypted.cpp`
- 影响:各端只在本地保存自己的 `last_version`,服务端不知道 STM32、K210、Qt 最后看到或确认到了哪个同步版本。设备长期离线、同步卡住、版本落后时,只能靠日志和现场排查,联调成本较高。
- 建议处理:
1. 增加 `device_sync_state` 表,记录 `client`、`last_seen_version`、`last_ack_version`、`last_seen_at`。
2. 在 `/api/v1/sync/state` 和 `/api/v1/sync/ack` 中更新设备状态。
3. 在 `/api/v1/system/storage` 中返回设备同步摘要。
4. 后续 Qt 可展示同步状态,但不要求作为首阶段必须项。
- 本次处理:服务端新增 `device_sync_state` 表;`/api/v1/sync/state` 记录设备最近看到的服务端版本,`/api/v1/sync/ack` 记录 ACK 版本;`/api/v1/system/storage` 返回 `device_sync_state` 摘要。
### R-018 公网 HTTP 明文传输缺少 HTTPS 或反向代理保护
- 优先级:P2
- 状态:已修复
- 位置:`server/server_encrypted.cpp`、部署配置
- 影响:服务端当前直接提供 HTTP 8082。若部署在公网,`X-Api-Token`、配置接口、照片上传下载和管理操作都会以明文传输,Token 可能被窃听或重放。AES 签到正文只覆盖 STM32 上传内容,不能保护管理 API 和设备同步 API。
- 建议处理:
1. 公网部署时使用 Nginx/Caddy 等反向代理终止 HTTPS。
2. 服务端尽量只监听本机或内网地址,由反向代理对外暴露。
3. 配置 HTTPS 后轮换 API Token 和相关密钥。
4. 文档中明确本机演示和公网部署的不同安全要求。
- 本次处理:服务端增加 `ATTENDANCE_BIND_HOST` 监听地址配置;文档新增公网部署安全要求,建议服务端绑定 `127.0.0.1` 并通过 Nginx/Caddy 终止 HTTPS 后再对外暴露。
### R-019 关键接口缺少自动化回归脚本
- 优先级:P3
- 状态:已修复
- 位置:`docs/06_后端接口Windows测试命令.md`、待新增测试脚本
- 影响:当前主要依赖手动 curl 文档验证接口。服务端、Qt、STM32、K210 协议经常一起变动,缺少脚本化回归会让鉴权、同步状态、时间段配置、照片接口和记录接口的破坏更晚被发现。
- 建议处理:
1. 保留现有手动 curl 文档,同时增加 PowerShell 自动化测试脚本。
2. 覆盖 health、storage、records、statistics、time-slots、sync/state、faces/photos 等低风险接口。
3. 对需要真实硬件或真实 AES 正文的接口保留手动验证说明。
4. 在每次服务端改动后运行脚本,记录测试结果。
- 本次处理:新增 `server/regression_api_tests.ps1`,覆盖 health、storage、time、sync、records、statistics、time-slots、duplicates、today、mappings、faces 等低风险接口;手动命令文档已加入脚本使用方式。
### R-020 删除 ACK 失败后本地版本仍前进,可能卡住删除任务
- 优先级:P1
- 状态:已修复
- 位置:`k210/main.py:1227-1239`
- 影响:K210 删除本地人脸后会向服务端发送同步 ACK,但当前没有判断 ACK 响应是否成功,就保存了 `server_version`。如果 ACK 请求丢包、超时或服务端未确认,服务端仍保留待删除任务;K210 下一次用已推进的 `last_version` 请求同步时可能得到 `changed:false`,导致该删除任务长期留在服务端,后续同步状态不一致。
- 建议处理:
1. 检查 `ack_resp` 是否包含成功标志,例如 `"success":true`。
2. 只有删除成功且 ACK 成功时,才保存新的 `sync_last_version`。
3. ACK 失败时保留旧版本,并打印日志,等待下一轮同步重试。
4. 可在服务端状态接口或 Qt 端观察 pending delete 数量,验证任务不会卡住。
- 备注:这是联调稳定性问题,不属于必须复杂化同步协议的工业级需求;毕业设计中用简单 ACK 成功判断即可。
- 本次处理:`k210/main.py` 在删除任务 ACK 后检查响应中是否包含 `"success":true`;ACK 失败时将本轮同步标记为失败,不保存新的本地同步版本,等待下一轮继续重试。
### R-021 多设备或缓存过期时重复签到仍可能漏判
- 优先级:P2
- 状态:已修复
- 位置:`Hardware/Attendance.c:520-529`
- 影响:当前签到处理主要检查 STM32 本地记录和开机/重连时下载的今日缓存。`Attendance_IsDuplicateOnServer` 已存在,但没有接入主签到路径。如果现场只有一台设备演示,影响较小;如果多台设备同时使用,或设备启动后缓存长时间未刷新,服务端已有的同一时段签到记录可能不会被当前设备发现,导致重复签到。
- 建议处理:
1. 在 `g_server_connected` 为真时,签到前调用一次 `Attendance_IsDuplicateOnServer`。
2. 查询失败时仍允许本地签到并进入待同步队列,避免弱网影响正常演示。
3. 时段切换、服务器重连成功后刷新今日记录缓存。
4. 如果只保留单设备演示,也应在论文或文档中说明本地缓存查重的适用范围。
- 备注:毕业设计阶段不需要做复杂分布式一致性,优先保证演示环境下行为可解释。
- 本次处理:`Hardware/Attendance.c` 在本地记录和缓存查重之后,服务器在线时调用 `Attendance_IsDuplicateOnServer` 做一次服务端查重;查询失败仍按未重复处理,避免弱网阻断演示。`User/main.c` 在 WiFi/HTTP 服务端重连成功后刷新时间段配置和今日记录缓存。
### R-022 HTTP GET 可能接受半截响应
- 优先级:P2
- 状态:已修复
- 位置:`Hardware/esp8266.c:486-498`
- 影响:`ESP8266_HttpGet` 在发现响应体后,如果超过约 3 秒且 `body_len > 0`,即使存在 `Content-Length` 且尚未收完整,也会复制响应并返回成功。弱网、ESP8266 分包或服务端响应稍慢时,STM32 可能拿到半截 JSON,进而导致时间段配置、同步状态或今日记录解析失败。
- 建议处理:
1. 如果响应头包含 `Content-Length`,必须等到 `body_len >= content_length` 才返回成功。
2. 只有没有 `Content-Length` 时,才使用“超时且已有响应体”的兜底逻辑。
3. 超时未收完整时返回 `ESP8266_FAIL`,让上层保留当前配置并下次重试。
4. 用较大的同步状态响应或人工延迟响应测试 ESP8266 分包场景。
- 备注:该项主要提升弱网稳定性,不影响当前本地小数据量演示的主体功能。
- 本次处理:`Hardware/esp8266.c` 的 GET 响应处理改为:只在没有 `Content-Length` 时使用“超时且已有响应体”的兜底逻辑;如果服务端返回了 `Content-Length`,必须等到响应体长度达到声明长度才返回成功。
## 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 构造。
1. 已完成 R-020:K210 删除 ACK 失败时不再推进本地同步版本,避免服务端待删除任务卡住。
2. 已完成 R-022:ESP8266 GET 在有 `Content-Length` 时必须收完整响应,减少弱网下配置同步解析失败。
3. 已完成 R-021:STM32 在线签到前补服务端查重,重连后刷新今日记录缓存。
4. R-002 仍需真实环境人工轮换已暴露凭据;代码层面已尽量外部配置化和脱敏。
5. 已修复项保持回归关注:R-001、R-003、R-004、R-005、R-006、R-007、R-008、R-009 至 R-022。
## 4. 验证记录
@@ -140,3 +337,8 @@
- `server/server_encrypted.cpp` 因当前本机缺少 OpenSSL 头文件,未完成语法编译验证。
- 现有 Keil 构建日志显示固件旧构建为 `0 Error(s), 0 Warning(s)`,但尚未在本次审查中重新编译固件。
- 2026-05-01:已完成主链路安全修复;旧版备用服务端与测试脚本已清理,避免误部署非鉴权接口。
- 2026-05-01:追加记录 R-009 至 R-014,作为后续优化和修复跟踪项。
- 2026-05-01:分析并合并 `docs/07_后续优化路线.md` 中仍需跟踪的项目,追加 R-015 至 R-019;同步删除该路线文档。
- 2026-05-01:完成 R-009 至 R-013、R-015 至 R-019 的代码和文档修复;新增服务端回归脚本 `server/regression_api_tests.ps1`。仍需上板验证 STM32/K210 运行时行为,并在真实部署中轮换 R-002 涉及的凭据。
- 2026-05-01:追加记录 R-020 至 R-022,作为后续优化跟踪项。
- 2026-05-01:完成 R-020 至 R-022 的代码修复;`k210/main.py` 已通过普通 Python 语法检查,STM32 相关改动仍需 Keil 编译和上板验证。
+47 -36
View File
@@ -17,15 +17,26 @@ http://127.0.0.1:8082
如果测试公网服务器,替换成:
```text
http://27.106.113.205:8082
https://your-domain.example
```
下面示例默认使用本机地址。
## 0. 自动化回归脚本
服务端启动后,优先运行脚本化检查:
```powershell
cd server
powershell -ExecutionPolicy Bypass -File .\regression_api_tests.ps1 -BaseUrl "http://127.0.0.1:8082" -ApiToken "change-me-token"
```
该脚本覆盖健康检查、存储状态、同步状态、记录、统计、时间段、映射和照片列表等低风险接口。需要真实硬件、真实 AES 正文或真实 JPEG 文件的接口仍按下方手动命令验证。
## 1. 测试端口和服务健康
```powershell
curl.exe -i --max-time 8 "http://27.106.113.205:8082/api/v1/system/health"
curl.exe -i --max-time 8 "http://127.0.0.1:8082/api/v1/system/health"
```
预期能看到 `HTTP/1.1 200 OK` 或类似成功响应。
@@ -33,13 +44,13 @@ curl.exe -i --max-time 8 "http://27.106.113.205:8082/api/v1/system/health"
## 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"
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://127.0.0.1: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"
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://127.0.0.1:8082/api/v1/system/storage"
```
重点看:
@@ -57,13 +68,13 @@ curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: applicati
## 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"
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://127.0.0.1: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"
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://127.0.0.1:8082/api/v1/sync/state?client=k210&last_version=0"
```
重点看:
@@ -78,7 +89,7 @@ curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: applicati
## 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"
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://127.0.0.1:8082/api/v1/sync/state?client=stm32&last_version=0"
```
重点看 `time_slots` 是否正确返回。
@@ -86,7 +97,7 @@ curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: applicati
## 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"
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://127.0.0.1:8082/api/v1/sync/state?client=qt&last_version=0"
```
## 2.4 K210 确认统一同步删除
@@ -94,13 +105,13 @@ curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: applicati
把 `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"
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://127.0.0.1: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"
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://127.0.0.1:8082/api/v1/attendance/records?limit=1000&offset=0"
```
后端当前以 `attendance.db` 为准。旧 `attendance_database.csv` 只会在首次启动 SQLite 时作为迁移来源,之后新增记录不会再写回 CSV。
@@ -120,13 +131,13 @@ 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"
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://127.0.0.1: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"
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://127.0.0.1:8082/api/v1/config/time-slots"
```
## 6. 保存签到时间段
@@ -134,49 +145,49 @@ curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: applicati
用于测试 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"
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://127.0.0.1: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"
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://127.0.0.1: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"
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://127.0.0.1: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"
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://127.0.0.1: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"
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://127.0.0.1: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"
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://127.0.0.1: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"
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://127.0.0.1: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"
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" "http://127.0.0.1:8082/api/v1/faces/photos/1" --output "%TEMP%\backend_face_1.jpg"
```
如果 `face_id=1` 存在,照片会保存到:
@@ -190,55 +201,55 @@ curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" "http://27.106.113.20
把 `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"
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://127.0.0.1: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"
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://127.0.0.1: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"
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://127.0.0.1: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"
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://127.0.0.1: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"
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://127.0.0.1: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"
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://127.0.0.1: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"
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: application/json" "http://127.0.0.1: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"
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://127.0.0.1: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"
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://127.0.0.1:8082/api/v1/sync/request/ack"
```
## 22. 加密签到接口
@@ -246,7 +257,7 @@ curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Accept: applicati
`/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"
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://127.0.0.1:8082/api/v1/attendance/checkins"
```
## 23. 删除单条考勤记录
@@ -254,7 +265,7 @@ curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "X-Encrypted: AES1
危险操作,会删除真实数据。执行前把时间和学生 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"
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -X DELETE "http://127.0.0.1:8082/api/v1/attendance/records?timestamp=2026-05-01%2014%3A30%3A38&student_id=1"
```
## 24. 删除指定人脸
@@ -262,7 +273,7 @@ curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -X DELETE "http://27.
危险操作,只删除人脸信息,不删除考勤记录。
```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"
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -X DELETE "http://127.0.0.1:8082/api/v1/faces/1"
```
## 25. 删除指定人脸并删除相关考勤记录
@@ -270,7 +281,7 @@ curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -X DELETE "http://27.
危险操作,会删除人脸和相关记录。
```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"
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -X DELETE "http://127.0.0.1:8082/api/v1/faces/1?delete_records=true"
```
## 26. 清空全部考勤记录
@@ -278,7 +289,7 @@ curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -X DELETE "http://27.
危险操作,会清空考勤数据库。
```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"
curl.exe -i --max-time 8 -H "X-Api-Token: change-me-token" -H "Content-Type: application/json" -X POST --data-binary "{}" "http://127.0.0.1:8082/api/v1/attendance/records/clear"
```
## 27. Windows 上检查端口占用
@@ -298,7 +309,7 @@ Test-NetConnection 127.0.0.1 -Port 8082
公网服务器测试:
```powershell
Test-NetConnection 27.106.113.205 -Port 8082
Test-NetConnection 127.0.0.1 -Port 8082
```
`TcpTestSucceeded : True` 表示 TCP 端口可连通。
@@ -308,7 +319,7 @@ Test-NetConnection 27.106.113.205 -Port 8082
先手动执行这条:
```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"
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://127.0.0.1:8082/api/v1/config/time-slots"
```
如果后端日志显示:
-272
View File
@@ -1,272 +0,0 @@
# 后续优化路线
本文档记录当前项目在“可继续优化但非当前阻塞”的方向,方便后续开发、答辩扩展或二次重构时逐项推进。
## 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 删除任务和设备同步状态表。