From e720b1a39270e8d2a8b99c98159374d00cc01f2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BA=AA=E7=94=B0=E9=87=8E?= Date: Mon, 4 May 2026 21:03:06 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=A0=E8=BE=93=E5=8A=A0=E5=AF=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 00_项目总文档.md | 48 ++++++------ 01_STM32固件端文档.md | 5 +- 02_K210识别端文档.md | 41 ++++++---- 03_服务端文档.md | 170 +++++++++++++++++++++++------------------- 04_Qt管理端文档.md | 77 ++++++++++--------- 5 files changed, 190 insertions(+), 151 deletions(-) diff --git a/00_项目总文档.md b/00_项目总文档.md index 4337444..eb37b56 100644 --- a/00_项目总文档.md +++ b/00_项目总文档.md @@ -7,9 +7,9 @@ | 部分 | 所在目录 | 主要职责 | 可以把它理解成 | | --- | --- | --- | --- | | STM32 固件端 | `stm32/` | 接收 K210 识别结果、判断是否允许签到、加密上传记录、控制 OLED/蜂鸣器/指示灯/补光灯 | 考勤机主控板 | -| K210 识别端 | `k210/` | 摄像头采集、人脸检测、人脸特征比对、添加/删除本地人脸、上传人脸照片、同步人脸映射 | 摄像头和 AI 识别模块 | -| C++ 服务端 | `server/` | 提供 HTTP API、保存考勤记录、保存配置、管理照片和同步状态 | 中心服务器 | -| Qt 管理端 | `QT/` | Windows/Android 图形管理界面,用于查看记录、统计、配置时间段、管理映射和照片 | 管理后台 | +| K210 识别端 | `k210/` | 摄像头采集、人脸检测、人脸特征比对、添加/删除本地人脸、上传人脸照片、同步人脸档案 | 摄像头和 AI 识别模块 | +| C++ 服务端 | `server/` | 提供 HTTP API、保存考勤记录、时间段、学生班级档案、人脸档案、照片和同步状态 | 中心服务器 | +| Qt 管理端 | `QT/` | Windows/Android 图形管理界面,用于查看记录、统计、配置时间段、管理班级、人脸照片和照片对应学生信息 | 管理后台 | 整个系统的目标是: @@ -26,14 +26,20 @@ K210 本地添加人脸时,会生成一个原始人脸编号,例如 `ID:1`、`ID:2`。这个编号只表示 K210 本地第几个录入的人脸。 -真实考勤需要的是学号,例如 `20240001`。所以项目里有一个“人脸 ID 到学号”的映射: +真实考勤需要的是学号,例如 `20240001`。所以项目里通过人脸档案维护“人脸 ID 到学号”的绑定: ```json { - "mappings": { - "1": "20240001", - "2": "20240002" - } + "face_profiles": [ + { + "face_id": "1", + "student_id": "20240001" + }, + { + "face_id": "2", + "student_id": "20240002" + } + ] } ``` @@ -63,15 +69,15 @@ attendance.db | 文件或目录 | 作用 | | --- | --- | -| `attendance.db` | 主数据库,保存记录、配置、映射、同步状态 | +| `attendance.db` | 主数据库,保存记录、配置、学生班级档案、人脸档案、同步状态 | | `faces/` | 人脸照片目录,保存 K210 上传的 `.jpg` | | `backups/` | 数据库备份目录 | -服务端数据以 SQLite 为准,考勤记录、配置、映射和同步状态都保存在 `attendance.db` 中。 +服务端数据以 SQLite 为准,考勤记录、时间段配置、学生班级档案、人脸档案和同步状态都保存在 `attendance.db` 中。 ### 2.4 同步版本号是什么 -服务端维护一个全局同步版本号 `sync_version`。当考勤记录、时间段、映射、人脸照片、删除任务等数据发生变化时,服务端会把版本号加一。 +服务端维护一个全局同步版本号 `sync_version`。当考勤记录、时间段、学生档案、班级信息、人脸档案、人脸照片、删除任务等数据发生变化时,服务端会把版本号加一。 设备同步时会带上自己上次同步到的版本: @@ -92,7 +98,7 @@ flowchart LR STM32 -->|"USART1 AT 指令"| ESP1["ESP8266"] ESP1 -->|"HTTP + AES 签到"| Server["C++ 服务端 8082"] K210 -->|"UART2 AT 指令"| ESP2["K210 侧 ESP8266"] - ESP2 -->|"HTTP 上传照片/同步映射"| Server + ESP2 -->|"HTTP 上传照片/同步人脸档案"| Server Qt["Qt 管理端 Windows/Android"] -->|"HTTP API"| Server Server --> SQLite["attendance.db"] Server --> Faces["faces/*.jpg"] @@ -154,14 +160,14 @@ POST /api/v1/faces/photos/ ``` 7. Qt 管理端进入“人脸管理”,刷新后可以看到照片。 -8. 管理员为该 face_id 填写真实学号并保存映射。 -9. 服务端保存映射并触发同步。 +8. 管理员在该 face_id 的编辑弹窗中填写真实学号、姓名和班级,并保存人脸档案。 +9. 服务端保存人脸档案并触发同步。 10. K210 拉取同步状态后更新本地 `face_mapping.txt`,识别发送时把 `face_id` 转成真实学号,`recordftr.txt` 中的原始 `ID:` 保持不变。 ### 4.3 删除人脸流程 1. Qt 管理端删除某个学生或人脸。 -2. 服务端删除照片、映射,按需要删除考勤记录。 +2. 服务端删除照片、解除人脸绑定,按需要删除考勤记录。 3. 服务端写入待删除任务 `pending_delete`,并提升同步版本。 4. K210 定期检查同步请求或拉取统一同步状态。 5. K210 在本地删除对应特征和照片。 @@ -251,7 +257,7 @@ K210 侧: ### 8.1 API Token -除少数公开接口外,服务端要求请求头中带 Token: +除 `OPTIONS` 预检请求外,服务端要求业务接口请求头中带 Token: ```text X-Api-Token: atk_nKfVWLhMjL0RGSfRll3R_sdJS6IrIjuV @@ -277,18 +283,18 @@ Qt 端也会读取同名环境变量;STM32 和 K210 目前是写在代码里 | 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` | -STM32 上传签到使用加密请求。Qt 当前普通记录和统计请求使用 JSON 明文响应,代码仍保留了解密能力。 +业务 JSON 接口统一使用 AES-128-CBC + PKCS#7 + Base64 传输。POST JSON 请求体需要携带 `X-Encrypted: AES128-CBC`,GET/DELETE/POST 的 JSON 响应由服务端加密后返回 `X-Encrypted: AES128-CBC`。K210 人脸照片 JPEG 因体积和分块传输限制保留原始二进制上传/下载,照片列表、同步状态、删除确认等 JSON 仍走加密链路。 ## 9. 推荐启动顺序 -1. 启动服务端,确认 `http://服务器IP:8082/api/v1/system/health` 返回健康状态。 +1. 启动服务端,用 Qt 或带 Token 与 AES 解密能力的客户端确认 `/api/v1/system/health` 返回健康状态。 2. 启动 Qt 管理端,在配置中心填写服务端地址和 API Token,点击“保存”后再点“测试连接”。 3. 在 Qt 管理端新增签到时间段并保存。 4. 烧录并启动 STM32,确认 OLED 显示系统就绪。 5. 启动 K210,确认摄像头画面、模型加载和 WiFi 连接正常。 6. 在 K210 添加人脸,等待照片上传到服务端。 -7. 在 Qt 管理端为照片对应的人脸 ID 填写真实学号并保存映射。 -8. K210 同步映射后,再让同一人进行识别签到。 +7. 在 Qt 管理端为照片对应的人脸 ID 点击“编辑”,填写真实学号、姓名和班级并保存人脸档案。 +8. K210 同步人脸档案后,再让同一人进行识别签到。 9. 在 Qt 管理端查看考勤记录和统计结果。 ## 10. 一键理解各端关系 @@ -297,7 +303,7 @@ STM32 上传签到使用加密请求。Qt 当前普通记录和统计请求使 | --- | --- | --- | | 改服务端地址 | Qt 配置中心、STM32/K210 代码常量 | 各端 HTTP 请求指向新服务器 | | 设置允许签到的时间 | Qt 配置中心 | Qt 保存到服务端,STM32 定期下载 | -| 给人脸绑定学号 | Qt 人脸管理 | 服务端保存映射,K210 同步后更新本地 `face_mapping.txt`,识别发送时转换成学号 | +| 给人脸维护学生信息 | Qt 人脸管理 | 服务端保存学生档案和人脸档案,K210 同步后更新本地 `face_mapping.txt`,识别发送时转换成学号 | | 添加人脸 | K210 按键短按 | K210 保存特征和照片,照片上传服务端 | | 删除人脸 | Qt 人脸管理或 K210 删除模式 | Qt 删除会生成服务端同步任务;K210 本地删除只影响本地 | | 查看记录 | Qt 考勤记录页 | Qt 从服务端读取最近记录 | diff --git a/01_STM32固件端文档.md b/01_STM32固件端文档.md index da8dced..ca49015 100644 --- a/01_STM32固件端文档.md +++ b/01_STM32固件端文档.md @@ -219,7 +219,7 @@ GET /api/v1/attendance/today?slot_start=08:00&slot_end=09:30 下载后,STM32 会把这些学号放进缓存。之后如果同一个学生在同一个时段再次识别,会被判定为重复签到。 -## 11. 加密上传 +## 11. 加密传输 STM32 上传签到记录时使用: @@ -227,6 +227,7 @@ STM32 上传签到记录时使用: POST /api/v1/attendance/checkins X-Api-Token: X-Encrypted: AES128-CBC +X-Encrypted-Response: true Content-Type: application/octet-stream ``` @@ -242,7 +243,7 @@ Content-Type: application/octet-stream } ``` -服务端解密成功后写入 `attendance.db`。 +服务端解密成功后写入 `attendance.db`,响应 JSON 也会以 AES-128-CBC + Base64 返回。STM32 的 `ESP8266_HttpGet` 会为时间、同步状态、查重和今日记录等 GET 请求带上 `X-Encrypted-Response: true`,接收后先解密,再把明文 JSON 交给上层解析。 ## 12. 本地记录和同步状态 diff --git a/02_K210识别端文档.md b/02_K210识别端文档.md index 384a6f0..6237d1e 100644 --- a/02_K210识别端文档.md +++ b/02_K210识别端文档.md @@ -169,21 +169,25 @@ UNKNOWN POST /api/v1/faces/photos/1 ``` -## 10. 人脸映射同步 +## 10. 人脸档案同步 -新增人脸后,K210 只知道它是 `ID:1`,不知道真实学号。管理员需要在 Qt 管理端把 `1` 映射成学号: +新增人脸后,K210 只知道它是 `ID:1`,不知道真实学号。管理员需要在 Qt 管理端把 `1` 绑定到学号,服务端通过 `/api/v1/face_profiles` 保存人脸档案: ```json { - "mappings": { - "1": "20240001" - } + "face_profiles": [ + { + "face_id": "1", + "student_id": "20240001", + "status": "active" + } + ] } ``` K210 同步后会: -1. 拉取服务端 `face_mappings`。 +1. 拉取服务端 `face_profiles`。 2. 修改本地 `face_mapping.txt`。 3. 保持 `recordftr.txt` 中的原始 `ID:` 不变。 4. 识别成功后先取原始 `face_id`,再通过 `face_mapping.txt` 转成真实学号发送给 STM32。 @@ -215,9 +219,14 @@ GET /api/v1/sync/state?client=k210&last_version=<本地版本> "version": 5, "changed": true, "state": { - "face_mappings": { - "1": "20240001" - }, + "face_profiles": [ + { + "face_id": "1", + "student_id": "20240001", + "status": "active", + "has_photo": true + } + ], "pending_delete": ["1"], "delete_tasks": [ { @@ -256,15 +265,15 @@ Qt 删除会在服务端创建待删除任务。K210 同步后删除本地人脸 ## 14. K210 常用接口 +K210 与服务端之间的业务 JSON 请求和响应使用 AES-128-CBC + PKCS#7 + Base64。`esp_http_get` 会请求加密响应并在本地解密,`esp_http_post_json` 会先加密 JSON 请求体再发送。人脸照片 JPEG 仍按 `image/jpeg` 原始二进制上传,避免在 K210/ESP8266 分块传输中引入过高内存占用。 + | 方法 | 路径 | 用途 | | --- | --- | --- | | `POST` | `/api/v1/faces/photos/` | 上传 JPEG 照片 | | `GET` | `/api/v1/faces/photos` | 查询服务端已有照片 | -| `GET` | `/api/v1/faces/mappings` | 拉取人脸 ID 到学号映射 | -| `GET` | `/api/v1/faces/pending-delete` | 拉取待删除列表 | -| `POST` | `/api/v1/faces/delete-ack` | 删除同步确认 | -| `GET` | `/api/v1/sync/state?client=k210&last_version=N` | 推荐统一同步 | -| `POST` | `/api/v1/sync/ack` | 推荐统一同步确认 | +| `GET` | `/api/v1/face_profiles` | 拉取人脸档案与学号绑定关系 | +| `GET` | `/api/v1/sync/state?client=k210&last_version=N` | 统一拉取人脸档案、删除任务与同步版本 | +| `POST` | `/api/v1/sync/ack` | 统一同步确认 | | `GET` | `/api/v1/sync/request` | 查询 Qt/服务端是否请求同步 | | `POST` | `/api/v1/sync/request/ack` | 确认收到同步请求 | @@ -277,7 +286,7 @@ Qt 删除会在服务端创建待删除任务。K210 同步后删除本地人脸 | 添加人脸失败 | 没检测到陌生人脸、SD 卡不可写、照片保存失败 | 检查光照、SD 卡和日志 | | Qt 看不到照片 | 上传队列未完成、服务端地址不一致、Token 错误 | 看 K210 `[PHOTO]`、`[NET]`、`[ESP]` 日志 | | 删除后仍识别 | K210 尚未同步或 ACK 失败 | 等待同步,检查 `/api/v1/sync/state` | -| 发给 STM32 的还是 `ID:1` | 映射没有保存或 K210 未同步 | Qt 保存映射后触发同步 | +| 发给 STM32 的还是 `ID:1` | 人脸档案没有保存或 K210 未同步 | Qt 保存人脸档案后触发同步 | ## 16. 修改配置时要注意 @@ -285,4 +294,4 @@ Qt 删除会在服务端创建待删除任务。K210 同步后删除本地人脸 2. K210 的 WiFi 配置和 STM32 可以相同,也可以不同,但都要能访问服务端。 3. K210 侧照片上传使用自己的 ESP8266,不依赖 STM32 的 ESP8266。 4. K210 本地 SD 卡数据很重要,删除或格式化 SD 卡会丢失本地人脸特征。 -5. 如果重新录入人脸,原 face_id 可能变化,需要重新在 Qt 管理端设置映射。 +5. 如果重新录入人脸,原 face_id 可能变化,需要重新在 Qt 管理端设置人脸档案绑定关系。 diff --git a/03_服务端文档.md b/03_服务端文档.md index 1decbc0..61d9da8 100644 --- a/03_服务端文档.md +++ b/03_服务端文档.md @@ -10,9 +10,9 @@ 2. 解密、校验并保存记录。 3. 提供考勤记录和统计数据给 Qt。 4. 保存签到时间段配置。 -5. 保存学生姓名映射和班级名称映射。 +5. 保存学生档案和班级信息。 6. 保存 K210 上传的人脸照片。 -7. 保存人脸 ID 到真实学号的映射。 +7. 保存人脸档案中的 face_id 与真实学号绑定关系。 8. 维护待删除人脸任务。 9. 维护全局同步版本号。 10. 提供健康检查、存储状态和时间接口。 @@ -79,20 +79,10 @@ idx_records_timestamp(timestamp) | key | value 内容 | | --- | --- | | `time_slots` | 时间段 JSON | -| `mapping_config` | 学生姓名、学生班级归属和班级名称映射 | | `sync_request` | K210 同步请求标志 | | `sync_version` | 全局同步版本号 | -### 4.3 `face_mappings` - -保存 K210 原始 face_id 到真实学号的映射。服务端同时维护结构化主表,避免把学生、照片和人脸绑定关系混在一个配置 JSON 里。 - -| 字段 | 含义 | -| --- | --- | -| `face_id` | K210 本地人脸编号 | -| `student_id` | 真实学号 | - -### 4.4 `classes` +### 4.3 `classes` 保存班级基础信息。 @@ -102,7 +92,7 @@ idx_records_timestamp(timestamp) | `class_name` | 班级名称 | | `updated_at` | 更新时间 | -### 4.5 `students` +### 4.4 `students` 保存学生基础信息。 @@ -115,7 +105,7 @@ idx_records_timestamp(timestamp) | `created_at` | 创建时间 | | `updated_at` | 更新时间 | -### 4.6 `face_profiles` +### 4.5 `face_profiles` 保存 K210 人脸实体与学生绑定关系。`face_id` 保持为 K210 原始编号,绑定学生时只更新 `student_id`,不改本地特征名。 @@ -128,7 +118,7 @@ idx_records_timestamp(timestamp) | `created_at` | 创建时间 | | `updated_at` | 更新时间 | -### 4.7 `face_photos` +### 4.6 `face_photos` 保存人脸照片元数据。照片文件仍保存在 `faces/.jpg`,不会按学号重命名。 @@ -140,7 +130,7 @@ idx_records_timestamp(timestamp) | `file_size` | 文件大小 | | `uploaded_at` | 上传时间 | -### 4.8 `pending_delete` +### 4.7 `pending_delete` 保存等待 K210 删除的任务。 @@ -153,7 +143,7 @@ idx_records_timestamp(timestamp) | `status` | 状态,默认 `pending` | | `ack_at` | 确认时间 | -### 4.9 `device_sync_state` +### 4.8 `device_sync_state` 保存各客户端同步状态。 @@ -186,23 +176,38 @@ $env:ATTENDANCE_CORS_ORIGIN="http://localhost" ## 6. 鉴权规则 -服务端对大多数接口要求请求头: +服务端对业务接口要求请求头: ```text X-Api-Token: ``` -允许不带 Token 的情况: +允许不带 Token 的情况只有 `OPTIONS` 预检请求。 -1. `OPTIONS` 预检请求。 -2. `GET /api/v1/system/health`。 - -除健康检查外,所有 `/api/v1/...` 接口都使用同样的鉴权逻辑。 +所有 `/api/v1/...` 业务接口都使用同样的鉴权逻辑,包括健康检查。 ## 7. 路由规范 服务端接口统一使用 `/api/v1/...` 路径。STM32、K210 和 Qt 管理端都通过这一套接口通信。 +## 7.1 加密传输规则 + +业务 JSON 接口统一使用 AES-128-CBC + PKCS#7 + Base64。JSON POST 请求体必须携带: + +```text +X-Encrypted: AES128-CBC +Content-Type: application/octet-stream +``` + +服务端会先解密请求体,再进入具体业务路由。JSON 响应统一加密返回,并携带: + +```text +X-Encrypted: AES128-CBC +Content-Type: application/octet-stream +``` + +人脸照片 JPEG 作为大体积二进制数据保留 `image/jpeg` 原始上传和下载;照片列表、上传结果、人脸档案、删除结果和同步状态等 JSON 数据仍然走加密传输。后续 API 小节中的 JSON 均表示解密后的业务结构,实际传输时按本节规则加密。 + ## 8. 核心 API 以下示例默认服务端地址是: @@ -215,6 +220,7 @@ http://127.0.0.1:8082 ```text GET /api/v1/system/health +X-Api-Token: ``` 返回示例: @@ -261,6 +267,7 @@ STM32 使用 `epoch` 转成 UTC+8 时间。 ```text POST /api/v1/attendance/checkins X-Encrypted: AES128-CBC +X-Encrypted-Response: true X-Api-Token: ``` @@ -319,13 +326,7 @@ GET /api/v1/attendance/records?limit=1000&offset=0 ] ``` -如果请求头带: - -```text -X-Encrypted-Response: true -``` - -服务端会把响应 JSON 加密后以 Base64 返回。Qt 当前普通记录请求没有启用这个请求头。 +服务端返回 AES 加密后的 Base64 字符串,客户端解密后得到上述 JSON 数组。 ### 8.6 获取统计 @@ -371,10 +372,11 @@ GET /api/v1/config/time-slots ```text POST /api/v1/config/time-slots -Content-Type: application/json +X-Encrypted: AES128-CBC +Content-Type: application/octet-stream ``` -请求体: +请求体解密后的 JSON 结构: ```json { @@ -422,32 +424,53 @@ GET /api/v1/attendance/today?slot_start=08:00&slot_end=09:30 STM32 开机后用它构建本地今日缓存。 -### 8.12 学生档案和班级名称映射 +### 8.12 学生档案 ```text -GET /api/v1/config/mappings -POST /api/v1/config/mappings +GET /api/v1/students +POST /api/v1/students ``` -格式: +返回或提交格式: ```json { - "student_name_map": { - "20240001": "张三" - }, - "student_class_map": { - "20240001": "1" - }, - "class_name_map": { - "1": "软件工程一班" - } + "students": [ + { + "student_id": "20240001", + "student_name": "张三", + "class_id": "1", + "status": "active" + } + ] } ``` -其中 `student_name_map` 维护学号到姓名,`student_class_map` 维护学号到班级编号,`class_name_map` 维护班级编号到班级名称。保存后会同步写入 `students` 和 `classes`,Qt 的记录列表、筛选、导出优先使用这里维护的学生班级归属。 +其中 `student_id` 为学号,`student_name` 为姓名,`class_id` 为所属班级编号。保存后服务端写入 `students` 表并提升同步版本。Qt 管理端修改学号前会弹窗提示该操作会影响人脸绑定、考勤显示、班级筛选和导出结果。 -### 8.13 人脸照片 +### 8.13 班级信息 + +```text +GET /api/v1/classes +POST /api/v1/classes +``` + +返回或提交格式: + +```json +{ + "classes": [ + { + "class_id": "1", + "class_name": "软件工程一班" + } + ] +} +``` + +保存班级时服务端写入 `classes` 表并提升同步版本。删除班级前会检查该班级下是否还有未删除学生;如果仍有学生,接口返回失败,避免产生悬空班级归属。 + +### 8.14 人脸照片 照片列表: @@ -483,38 +506,35 @@ GET /api/v1/faces/photos/1 成功时返回 `image/jpeg`。 -### 8.14 人脸 ID 映射 +### 8.15 人脸档案 ```text -GET /api/v1/faces/mappings -POST /api/v1/faces/mappings +GET /api/v1/face_profiles +POST /api/v1/face_profiles ``` -格式: +返回或提交格式: ```json { - "mappings": { - "1": "20240001" - } + "face_profiles": [ + { + "face_id": "1", + "student_id": "20240001", + "device_id": "k210-01", + "status": "active", + "has_photo": true + } + ] } ``` -保存映射后服务端会: +保存人脸档案后服务端会: -1. 写入兼容表 `face_mappings`。 -2. 同步更新结构化表 `face_profiles` 和 `students`。 -3. 对已取消绑定的人脸仅清空 `student_id`,保留原始照片和 `face_id`。 -4. 提升同步版本。 -5. 设置 K210 同步请求标志。 - -### 8.15 获取全部学生 ID - -```text -GET /api/v1/faces/student-ids -``` - -从 `students`、`records` 和人脸映射中汇总学生 ID。 +1. 写入结构化表 `face_profiles`。 +2. 对已取消绑定的人脸仅清空 `student_id`,保留原始照片和 `face_id`。 +3. 提升同步版本。 +4. 设置 K210 同步请求标志。 ### 8.16 删除人脸 @@ -526,7 +546,7 @@ DELETE /api/v1/faces/20240001?delete_records=true 服务端会: 1. 删除相关照片。 -2. 删除相关映射。 +2. 解除 `face_profiles` 中的人脸绑定。 3. 如果 `delete_records=true`,删除该学生相关考勤记录。 4. 创建 K210 待删除任务。 5. 提升同步版本。 @@ -538,7 +558,7 @@ DELETE /api/v1/faces/20240001?delete_records=true { "success": true, "photo_deleted": true, - "mapping_deleted": true, + "profile_unbound": true, "records_deleted": 3, "k210_pending": true, "sync_requested": true @@ -573,8 +593,8 @@ GET /api/v1/sync/state?client=&last_version= | client | 服务端返回内容 | | --- | --- | | `stm32` | `time_slots` | -| `k210` | `face_mappings`、`pending_delete`、`delete_tasks` | -| `qt` | 记录、统计、时间段、映射、照片列表、学生 ID、人脸映射 | +| `k210` | `face_profiles`、`pending_delete`、`delete_tasks` | +| `qt` | 记录、统计、时间段、学生档案、班级信息、人脸档案、照片列表、学生 ID | | `all` 或空 | 尽可能返回全部 | 如果没有变化: @@ -597,7 +617,7 @@ GET /api/v1/sync/state?client=&last_version= "client": "k210", "server_time": "2026-05-01 10:00:00", "state": { - "face_mappings": {}, + "face_profiles": [], "pending_delete": [], "delete_tasks": [] } @@ -657,7 +677,7 @@ cd D:\University\graduation\thesis\code\server ## 11. 安全注意事项 1. 默认 Token 已写在代码里,不适合直接暴露公网。 -2. 当前是 HTTP 明文传输,公网部署建议放到 HTTPS 反向代理后面。 +2. 业务 JSON 已使用应用层 AES 加密,公网部署仍建议放到 HTTPS 反向代理后面,保护照片二进制、Token 和传输元数据。 3. AES Key/IV 是固定值,演示可以,生产环境不建议固定写死。 4. SQLite 数据库和照片目录需要定期备份。 5. `ATTENDANCE_CORS_ORIGIN` 不建议设置成任意来源。 diff --git a/04_Qt管理端文档.md b/04_Qt管理端文档.md index ae64d70..0bbeaab 100644 --- a/04_Qt管理端文档.md +++ b/04_Qt管理端文档.md @@ -12,9 +12,9 @@ Qt 管理端是整个系统的图形化后台。它支持 Windows 桌面端和 A 6. 导出统计 CSV。 7. 配置服务端地址和 API Token。 8. 配置签到时间段。 -9. 配置学号到姓名、学号到班级编号、班级编号到班级名的学生档案映射。 +9. 维护班级信息。 10. 查看 K210 上传的人脸照片。 -11. 给 K210 face_id 绑定真实学号。 +11. 编辑 K210 face_id 对应的学号、姓名和班级。 12. 删除人脸信息,并可选择删除相关考勤记录。 ## 2. 相关文件 @@ -26,11 +26,12 @@ Qt 管理端是整个系统的图形化后台。它支持 Windows 桌面端和 A | `QT/src/AppController.*` | QML 与 C++ 业务逻辑的中枢 | | `QT/src/ApiClient.*` | HTTP API 客户端 | | `QT/src/AttendanceTypes.h` | 考勤记录、统计、时间段结构体 | -| `QT/src/CryptoUtils.*` | AES 解密工具 | +| `QT/src/CryptoUtils.*` | AES 加解密工具 | | `QT/src/RecordListModel.*` | 考勤记录模型 | | `QT/src/StatisticsListModel.*` | 统计模型 | | `QT/src/TimeSlotListModel.*` | 时间段模型 | -| `QT/src/MappingListModel.*` | 学生姓名、学生班级归属、班级名称映射模型 | +| `QT/src/StudentProfileListModel.*` | 学生档案模型 | +| `QT/src/MappingListModel.*` | 班级信息等键值模型 | | `QT/src/FaceListModel.*` | 人脸管理模型 | | `QT/CMakeLists.txt` | Qt/CMake 构建配置 | | `QT/clear_qt_cache.ps1` | 清理当前 Qt 项目的 Qt Creator/CMake 缓存 | @@ -71,8 +72,8 @@ Qt 管理端是单窗口应用,共 5 个页面: | --- | --- | | 考勤记录 | 查看、筛选、导出、清空记录 | | 统计分析 | 按时间范围统计学生签到情况 | -| 配置中心 | 服务端地址、签到时间段、名称映射 | -| 人脸管理 | 查看照片、绑定学号、删除人脸 | +| 配置中心 | 服务端地址、签到时间段、班级管理 | +| 人脸管理 | 查看照片、编辑学号姓名班级、删除人脸 | | 关于 | 系统说明、版本和作者信息 | PC 宽屏显示左侧导航栏;窄屏和 Android 显示顶部/底部更紧凑的导航布局。 @@ -107,6 +108,8 @@ X-Api-Token: Accept: application/json ``` +除照片 JPEG 下载外,Qt 的业务 JSON 请求和响应统一使用 AES-128-CBC + PKCS#7 + Base64。GET/DELETE 请求要求服务端返回加密响应,POST JSON 会先加密请求体并携带 `X-Encrypted: AES128-CBC`;照片内容仍按 `image/jpeg` 原始二进制显示。 + Token 默认值写在 `QT/src/ApiClient.cpp`,也可以通过环境变量覆盖: ```powershell @@ -127,7 +130,7 @@ Qt 启动后: 2. 设置应用图标、应用名、版本号、组织名。 3. Android 下启用原生文字渲染。 4. 创建 `AppController`。 -5. `AppController` 读取本地服务端地址、API Token、映射配置、时间段配置。 +5. `AppController` 读取本地服务端地址、API Token、学生档案缓存、班级缓存、人脸档案缓存和时间段配置。 6. `ApiClient` 设置当前服务端地址和 API Token。 7. 启动自动重连状态并执行健康检查。 8. QML 载入 `Main.qml`。 @@ -181,18 +184,18 @@ GET /api/v1/attendance/records?limit=1000&offset=0 | --- | --- | | 班级 | `class_id` 匹配 | | 学号 | `student_id` 匹配 | -| 姓名 | 映射姓名或原始 name 包含关键字 | +| 姓名 | 学生档案姓名或原始 name 包含关键字 | | 日期 | 全部、今天、最近 7 天、最近 30 天、指定日期 | 姓名显示优先级: -1. 如果学生映射里有该学号,显示映射姓名。 +1. 如果学生档案里有该学号,显示档案姓名。 2. 否则显示服务端记录中的 `name`。 3. 如果都没有,则显示原始学号。 班级显示优先级: -1. 如果班级映射里有该 `class_id`,显示映射班级名。 +1. 如果班级信息里有该 `class_id`,显示班级名称。 2. 否则显示 `class_id`。 ## 11. 记录导出 @@ -244,7 +247,7 @@ GET /api/v1/attendance/records?limit=1000&offset=0 1. 服务端地址和 API Token。 2. 时间段配置。 -3. 名称映射配置。 +3. 班级管理。 ### 13.1 服务端配置 @@ -294,37 +297,36 @@ timeslot_config.json 该文件只作为 Qt 管理端本地缓存;服务端当前以 SQLite 中的配置为准。 -### 13.3 学生档案映射配置 +### 13.3 班级管理 -学号到姓名: +配置中心只负责班级新建、命名和删除,不再单独提供学生档案编辑页。学生学号、姓名和班级归属在人脸管理页的编辑弹窗中维护。班级信息通过独立接口维护: + +```text +GET /api/v1/classes +POST /api/v1/classes +``` + +请求体示例: ```json { - "20240001": "张三" + "classes": [ + { + "class_id": "1", + "class_name": "软件工程一班" + } + ] } ``` -学号到班级编号: +记录列表、班级筛选和导出优先使用学生档案中的班级归属关系;没有配置时才显示设备上传记录中的原始班级编号。 -```json -{ - "20240001": "1" -} -``` +删除班级前,管理端会先检查该班级下是否还有学生;服务端保存班级时也会进行同类校验。 -班级编号到班级名称: - -```json -{ - "1": "软件工程一班" -} -``` - -记录列表、班级筛选和导出优先使用“学号到班级编号”的归属关系;没有配置时才显示设备上传记录中的原始班级编号。 本地文件: ```text -mapping_config.json +profiles_cache.json ``` ## 14. 人脸管理页 @@ -332,17 +334,18 @@ mapping_config.json 人脸管理页把两类人脸数据合并展示: 1. 服务端照片列表。 -2. 服务端 face_id 到学生 ID 映射。 +2. 服务端人脸档案。 -照片 ID 始终使用 K210 原始 `face_id`,例如 `faces/1.jpg`。Qt 只维护 `face_id -> 学号` 的绑定关系,不再把所有已有考勤学生 ID 当成“无照片人脸”展示。 +照片 ID 始终使用 K210 原始 `face_id`,例如 `faces/1.jpg`。Qt 通过 `/api/v1/face_profiles` 维护 `face_id -> 学号` 的绑定关系,并通过 `/api/v1/students` 保存学号、姓名和班级归属,不再把所有已有考勤学生 ID 当成“无照片人脸”展示。 用户可以: 1. 刷新人脸库。 -2. 为每张照片填写真实学号。 -3. 保存映射。 +2. 点击“编辑”打开弹窗,填写真实学号、姓名和班级。 +3. 保存学生档案和人脸档案。 4. 删除人脸。 -5. 删除人脸时选择是否同时删除该学生的考勤记录。 +5. 修改已有学号前弹窗提示影响范围。 +6. 删除人脸时选择是否同时删除该学生的考勤记录。 照片下载流程: @@ -409,7 +412,7 @@ server/apiToken | 文件 | 内容 | | --- | --- | | `timeslot_config.json` | 时间段配置 | -| `mapping_config.json` | 学生姓名、学生班级归属和班级名称映射 | +| `profiles_cache.json` | 学生档案、班级信息和人脸档案的本地缓存 | | `face_photos/*.jpg` | 下载后的人脸照片缓存 | ## 18. 构建方式 @@ -456,4 +459,4 @@ powershell -ExecutionPolicy Bypass -File .\clear_qt_cache.ps1 | 人脸照片不显示 | 下载失败、本地缓存路径无效、服务端无照片 | 刷新人脸库,看服务端 `faces/` | | 统计数量少于服务端总量 | Qt 默认只拉取 1000 条记录 | 增加分页或服务端统计接口 | | 时间段为空导致无法签到 | 当前默认就是空配置 | 在配置中心新增时间段并保存 | -| 保存映射后 K210 没变化 | K210 尚未同步或 WiFi 不通 | 等待同步或检查 K210 日志 | +| 保存人脸档案后 K210 没变化 | K210 尚未同步或 WiFi 不通 | 等待同步或检查 K210 日志 |