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