更新文档

This commit is contained in:
2026-05-01 23:03:42 +08:00
parent 5a31ff2179
commit a32baa96e3
14 changed files with 2799 additions and 1853 deletions
+229 -290
View File
@@ -1,344 +1,283 @@
# 项目审查问题清单
# 项目维护、验收与风险清单
审查日期:2026-05-01
## 1. 文档目的
本文档用于记录当前项目中已发现的高风险问题,并作为后续逐项修复的跟踪清单。状态说明:
这份文档用于回答三个问题:
- `待处理`:尚未开始修复。
- `处理中`:已经开始改动或验证。
- `已修复`:代码已修复并通过基本验证。
- `暂缓`:因演示环境、硬件依赖或其他原因暂不处理。
1. 项目目前实现了什么。
2. 验收或答辩时可以从哪些方面说明。
3. 后续维护时有哪些风险和改进方向。
## 1. 优先级概览
它不是单纯的 bug 列表,而是把功能完整性、稳定性、安全性、易用性和可维护性都列出来。
| 编号 | 优先级 | 状态 | 模块 | 问题 |
| --- | --- | --- | --- | --- |
| 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 未编码 |
| 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. 当前实现状态总览
## 2. 详细问题
| 模块 | 当前状态 | 说明 |
| --- | --- | --- |
| STM32 主控 | 已实现 | 接收 K210、考勤判断、加密上传、断线重试、时间段同步 |
| K210 识别 | 已实现 | 人脸检测、特征比对、添加/删除、照片上传、映射同步 |
| 服务端 | 已实现 | SQLite 存储、HTTP API、Token、AES 解密、统一同步 |
| Qt 管理端 | 已实现 | 记录、统计、配置、人脸管理、PC/Android 响应式界面 |
| 默认时间段 | 已改为空 | 需要管理员在 Qt 中新增 |
| 数据导出 | 已实现 | Qt 可导出记录和统计 CSV |
| 自动化 API 回归 | 已有脚本 | `server/regression_api_tests.ps1` |
### R-001 公网接口无鉴权且允许任意来源
## 3. 功能验收清单
- 优先级: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`。
### 3.1 服务端验收
### R-002 明文密钥和凭据已进入项目文件
| 检查项 | 通过标准 |
| --- | --- |
| 服务可启动 | 控制台显示监听 `8082` |
| 健康检查 | `/api/v1/system/health` 返回 `healthy` |
| SQLite 初始化 | 生成或打开 `attendance.db` |
| 时间接口 | `/api/v1/system/time` 返回 `epoch` 和 `server_time` |
| Token 鉴权 | 不带 Token 访问受保护接口返回 401 |
| 时间段默认 | `/api/v1/config/time-slots` 返回 `{"time_slots":[]}` |
| 记录接口 | `/api/v1/attendance/records` 返回 JSON 数组 |
| 统计接口 | `/api/v1/attendance/statistics` 返回 `total`、`today` |
| 照片接口 | 可列出、上传、下载 JPEG |
| 同步接口 | `/api/v1/sync/state` 按客户端返回不同数据 |
- 优先级: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 密码、公网地址改为占位配置;Qt 关于页移除固定演示账号密码;日志中的疑似 API Key 已脱敏;Qt 默认服务地址改为本机地址。仍需在真实环境中轮换已暴露的 WiFi、API Key、演示账号和 AES Key/IV。
### 3.2 Qt 管理端验收
### R-003 `face_id` 可造成路径穿越写读删文件
| 检查项 | 通过标准 |
| --- | --- |
| 启动界面 | Windows 和 Android 都能打开主界面 |
| 服务地址 | 可保存地址并测试连接 |
| 配置页布局 | PC 和 Android 不出现按钮遮挡 |
| 时间段 | 默认为空,可新增、保存、同步、清空 |
| 映射 | 可新增学生姓名和班级名称映射 |
| 记录页 | 能加载、筛选和导出记录 |
| 统计页 | 能按日期范围统计并导出 |
| 人脸管理 | 能刷新照片、绑定学号、保存映射、删除人脸 |
| 自动重连 | 服务端恢复后能重新同步 |
- 优先级:P1
- 状态:已修复
- 位置:`server/server_encrypted.cpp:837`
- 影响:`face_id` 直接来自 URL 并拼接成 `faces/<face_id>.jpg`,用于写入、读取和删除。包含路径分隔符或 `..` 时,可能越过 `faces` 目录操作其他文件。
- 建议处理:
1. 增加 `face_id` 白名单校验,只允许数字、字母、下划线、短横线等明确字符。
2. 对最终路径做规范化检查,确认仍位于 `faces` 目录下。
3. 所有照片读写删入口共用同一个安全路径构造函数。
- 本次处理:主服务端已增加 `isSafeId` 与 `buildFacePhotoPath`,照片读写删和重命名路径统一走安全 ID 校验。
### 3.3 STM32 验收
### R-004 大请求体会被静默截断
| 检查项 | 通过标准 |
| --- | --- |
| 硬件初始化 | OLED、蜂鸣器、LED、串口正常 |
| ESP8266 | 能连接 WiFi 和服务端 |
| 时间同步 | 能从服务端获取时间或读取 DS1302 |
| 时间段同步 | 能拉取服务端配置,空配置会清空本地时间段 |
| K210 接收 | 能解析 `ID:...,NAME:...,CONF:...` |
| 签到判断 | 置信度、时间段、重复签到逻辑生效 |
| 加密上传 | 服务端能收到并解密保存记录 |
| 断线重试 | 网络断开时记录保留,恢复后同步 |
- 优先级: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。
### 3.4 K210 验收
### R-005 CSV/JSON 没有转义会破坏数据和响应
| 检查项 | 通过标准 |
| --- | --- |
| 模型加载 | 三个 `.smodel` 正常加载 |
| 摄像头 | LCD 能显示画面,方向正确 |
| 人脸识别 | 已录入人脸能识别,陌生人显示陌生 |
| 添加人脸 | 短按进入添加,确认后写入 SD 卡 |
| 删除人脸 | 长按进入删除,可删除单个或全部 |
| 照片上传 | 新增人脸照片能上传到服务端 |
| 映射同步 | Qt 保存映射后 K210 本地名称更新 |
| 删除同步 | Qt 删除后 K210 能删除本地特征并 ACK |
- 优先级: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 仅作为首次导入来源。
## 4. 数据一致性清单
### R-006 `sscanf` 未限制宽度,UART 输入可溢出栈缓冲
### 4.1 Token 一致
- 优先级:P1
- 状态:已修复
- 位置:`Hardware/K210_USART.c:80`
- 影响:`%[^,]` 没有宽度限制,但目标缓冲只有 `id[20]` 和 `name[32]`。串口收到异常长字段时可能覆盖栈内存,造成固件异常。
- 建议处理:
1. 将格式改为 `%19[^,]`、`%31[^,]`。
2. 对接收缓冲做最大帧长、换行结束符和字段合法性校验。
3. 异常帧直接丢弃并清空缓冲。
- 本次处理:`Hardware/K210_USART.c` 和示例文件中的 `sscanf` 已增加字段宽度限制。
这些地方必须一致:
### R-007 多个 `std::stoi` 可被非法参数打崩服务线程
1. 服务端 `ATTENDANCE_API_TOKEN` 或默认 Token。
2. Qt 环境变量或默认 Token。
3. `User/main.c` 的 `HTTP_API_TOKEN`。
4. `k210/main.py` 的 `API_TOKEN`。
- 优先级:P2
- 状态:已修复
- 位置:`server/server_encrypted.cpp:1310`
- 影响:`limit`、`offset`、时间段参数直接调用 `std::stoi`,没有异常捕获和范围校验。非法请求参数可能让请求处理线程异常终止。
- 建议处理:
1. 增加安全整数解析函数。
2. 参数非法时返回 400。
3. 限制 `limit`、`offset` 和时间范围。
4. 所有 URL 参数统一走校验函数。
- 本次处理:主服务端已增加安全整数和时间解析,`limit`、`offset`、时间段和记录时间解析不再直接裸用 `std::stoi`。
### 4.2 服务端地址一致
### R-008 删除人脸 URL 未编码
这些地方要指向同一个服务端:
- 优先级:P2
- 状态:已修复
- 位置:`QT/src/ApiClient.cpp:453`
- 影响:Qt 端删除人脸时直接把 `studentId` 拼进路径。如果学号包含空格、`#`、`?`、`/` 或中文,URL 会被截断或路由错乱。
- 建议处理:
1. 使用 `QUrl::toPercentEncoding` 编码路径段。
2. 服务端接收后进行 URL 解码和 ID 校验。
3. 为删除人脸接口补充异常 ID 的测试用例。
- 本次处理:Qt 删除人脸路径段已使用 `QUrl::toPercentEncoding` 编码。
1. Qt 配置中心的服务端地址。
2. `User/main.c` 的 `HTTP_SERVER_IP` 和 `HTTP_SERVER_PORT`。
3. `k210/main.py` 的 `SERVER_IP` 和 `SERVER_PORT`。
### R-009 NTP-only 模式下时间不会前进
### 4.3 AES 参数一致
- 优先级: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 发送成功判断过早,可能静默丢签到
1. `Hardware/Crypto.c`
2. `server/server_encrypted.cpp`
3. `QT/src/CryptoUtils.cpp`
- 优先级: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 今日缓存不区分时段,会误判重复签到
```text
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
```
- 优先级: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` 的缓存项增加时段绑定;新增签到和服务器下载缓存都写入当前时段;不在时段内不再拉取全天缓存;时段切换时自动清空缓存。
### 4.4 时间段一致
### R-012 签到请求手写 JSON 解析会被特殊字符破坏
Qt 保存时间段后:
- 优先级:P1
- 状态:已修复
- 位置:`server/server_encrypted.cpp:707-742`
- 影响:`parseJSON` 直接按逗号拆字段、按第一个冒号拆键值,无法正确处理字符串里的逗号、冒号、反斜杠和转义引号。签到姓名、状态或后续字段一旦包含这些字符,会解析错列甚至写入错误数据。
- 建议处理:
1. 引入 `nlohmann/json` 等成熟 JSON 库,优先替换签到请求体解析。
2. 对必填字段做类型、长度和取值校验。
3. JSON 解析失败时返回 400,避免写入半解析数据。
4. 增加包含逗号、冒号、引号和反斜杠的解析测试样例。
- 本次处理:`server/server_encrypted.cpp` 的签到 JSON 解析改为逐字符解析字符串字面量,支持逗号、冒号、反斜杠和转义引号;签到字段增加 ID、长度、置信度范围和状态取值校验,非法请求返回 400。
1. 服务端 `time_slots` 更新。
2. 服务端同步版本提升。
3. STM32 下一次同步拉取到新时间段。
4. STM32 使用新的时间段判断签到。
### R-013 K210 从 SD 卡加载特征时使用 `eval`
### 4.5 人脸映射一致
- 优先级: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`,只接受浮点数组允许字符,并校验向量长度,异常记录会被跳过并打印解析错误。
Qt 保存人脸映射后:
### R-014 总文档指向不存在的 Qt 工程文件
1. 服务端保存 `face_mappings`。
2. 服务端触发 K210 同步请求。
3. K210 同步后更新 `face_mapping.txt`。
4. K210 更新 `recordftr.txt`。
5. K210 发给 STM32 的 ID 变成真实学号。
- 优先级: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` 前同步清理了相关引用。
## 5. 当前已知限制
### R-015 数据库缺少自动备份与恢复流程
### 5.1 Qt 记录统计基于当前加载记录
- 优先级: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` 已补恢复和校验流程。
Qt 默认请求:
### R-016 删除任务缺少 task_id 和状态跟踪
```text
limit=1000&offset=0
```
- 优先级: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` 确认。
如果服务端已有超过 1000 条记录,Qt 本地筛选和统计只覆盖当前加载的这 1000 条。服务端 `/statistics` 的总数和今日数仍来自数据库。
### 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` 摘要。
1. 增加分页。
2. 增加服务端按条件查询。
3. 增加服务端按日期统计接口。
### R-018 公网 HTTP 明文传输缺少 HTTPS 或反向代理保护
### 5.2 时间输入缺少强校验
- 优先级: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 后再对外暴露。
Qt 时间段输入框允许手动输入文本。当前主要依赖 `QTime::fromString("HH:mm")` 解析。
### 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 等低风险接口;手动命令文档已加入脚本使用方式。
1. 使用时间选择控件。
2. 保存前校验格式。
3. 检查开始时间必须早于结束时间。
4. 检查时间段重叠。
### R-020 删除 ACK 失败后本地版本仍前进,可能卡住删除任务
### 5.3 K210 JSON 解析仍偏轻量
- 优先级: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 失败时将本轮同步标记为失败,不保存新的本地同步版本,等待下一轮继续重试。
K210 MicroPython 环境中很多 JSON 解析是手写字符串处理。简单数据够用,但复杂转义字符支持有限。
### 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 服务端重连成功后刷新时间段配置和今日记录缓存。
1. 尽量让服务端返回简单结构。
2. 避免映射 key/value 中出现引号、逗号等复杂字符。
3. 如果固件环境支持,使用 JSON 库。
### R-022 HTTP GET 可能接受半截响应
### 5.4 STM32 本地缓存容量有限
- 优先级: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`,必须等到响应体长度达到声明长度才返回成功。
STM32 本地记录最多 100 条,今日学生缓存最多 50 个 ID。
## 3. 建议修复顺序
改进方向:
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。
1. 根据实际班级人数扩大缓存。
2. 增加外部 Flash 或 SD 卡存储。
3. 对已同步记录做更明确的淘汰策略。
## 4. 验证记录
### 5.5 服务端是单文件原生 HTTP 实现
服务端用 socket 手写 HTTP,演示和毕业设计足够直观,但在生产环境上不如成熟 Web 框架。
改进方向:
1. 引入成熟 HTTP 库。
2. 增加结构化日志。
3. 增加配置文件。
4. 增加单元测试。
## 6. 安全风险和建议
| 风险 | 当前状态 | 建议 |
| --- | --- | --- |
| 默认 Token 写在代码中 | 存在 | 部署时用环境变量,并替换 STM32/K210 常量 |
| HTTP 明文传输 | 存在 | 公网部署加 HTTPS 反向代理 |
| AES Key/IV 固定 | 存在 | 演示可接受,生产应使用更安全的密钥管理 |
| 管理接口权限粗粒度 | 存在 | 增加用户登录、角色权限 |
| 清空记录接口风险高 | 存在 | 增加二次确认、审计日志 |
| 删除人脸会影响多端 | 已有同步任务 | 保留日志,避免误删 |
## 7. 答辩或展示可以讲的重点
### 7.1 多端协同
项目不是单独做一个 UI 或一个识别脚本,而是把嵌入式主控、AI 识别、服务端、桌面/移动管理端串起来。
### 7.2 安全传输
STM32 上传签到数据不是明文,而是 AES-128-CBC 加密后 Base64 传输。服务端负责解密和校验。
### 7.3 同步版本机制
服务端维护统一版本号,STM32、K210、Qt 都可以按版本拉取自己需要的数据,减少无意义请求。
### 7.4 断线容错
STM32 在网络异常时仍能运行,并保留待同步记录;K210 在照片上传失败时保存本地照片并重新排队。
### 7.5 跨平台管理端
Qt 管理端支持 PC 和 Android,配置页已做响应式布局,避免窄屏按钮遮挡。
### 7.6 数据可视化和导出
Qt 提供记录筛选、统计分析和 CSV 导出,方便后续汇总考勤数据。
## 8. 回归测试建议
### 8.1 每次改服务端后
1. 启动服务端。
2. 运行 `server/regression_api_tests.ps1`。
3. 手动测试上传时间段为空数组。
4. 手动测试保存人脸映射后同步版本是否变化。
5. 手动测试删除人脸后 `pending_delete` 是否出现任务。
### 8.2 每次改 Qt 后
1. 检查 PC 宽屏。
2. 检查 PC 窄屏。
3. 检查 Android 手机宽度。
4. 检查配置中心展开/收起。
5. 检查时间段为空时页面是否正常。
6. 检查保存映射和刷新照片。
### 8.3 每次改 STM32 后
1. 看调试串口启动日志。
2. 确认 ESP8266 能连 WiFi。
3. 确认能下载时间段。
4. 确认空时间段时不会签到。
5. 配置时间段后再确认能签到。
6. 断开服务端再恢复,检查待同步记录上传。
### 8.4 每次改 K210 后
1. 确认模型加载。
2. 确认添加人脸写入 SD 卡。
3. 确认照片进入上传队列。
4. 确认服务端能看到照片。
5. 确认 Qt 保存映射后 K210 能同步。
6. 确认 Qt 删除后 K210 能删除并 ACK。
## 9. 建议后续优化路线
优先级从高到低:
1. 增加 Qt 时间输入校验和重叠检测。
2. 给服务端增加配置文件,减少硬编码。
3. 给服务端增加更完整的自动化测试。
4. 给 Qt 增加记录分页。
5. 给服务端增加按条件查询和统计接口。
6. 把 Token 和 AES 密钥改成部署时配置。
7. 增加 HTTPS 部署说明。
8. 给 STM32 增加更大的离线缓存。
9. 给 K210 使用更稳健的 JSON 解析。
10. 增加服务端日志文件轮转。
- `k210/main.py`、`k210/view_attendance.py` 已通过普通 Python 语法检查;MaixPy 运行时仍需上板验证。
- `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 编译和上板验证。