基于Android与百度云的人脸识别考勤系统:从架构设计到工程实践
2026/9/16 20:04:53 网站建设 项目流程

简介:这是一套面向计算机专业本科生及初学者的完整人脸识别考勤系统实战项目,聚焦Android移动端开发与云端AI能力集成,解决传统考勤效率低、代签漏洞等问题。资源包含328个文件,9.39MB压缩包,涵盖46个Java核心业务逻辑代码、31个XML界面布局、30个PNG图标资源、22个JS前端交互脚本、11个HTML管理页面以及关键的SQL数据库脚本和SpringBoot后端配置文件,结构清晰覆盖安卓客户端、百度智能云接口调用、MySQL数据存储与Web管理后台全链路。已有62人学习下载,项目源自高分毕业设计(答辩平均96分),提供可直接运行的app-release.apk、完整README说明文档、管理员账号(admin/123456)及百度云人脸同步机制实现细节,特别适合毕设选题、课程设计或Android+AI融合开发入门实践。

1. 项目概述与核心价值

最近几年,无论是高校还是培训机构,学生考勤管理一直是个让人头疼的“老大难”问题。传统的点名、刷卡、指纹签到,要么效率低下,要么存在代签、漏签的漏洞。我前阵子接手了一个学校的信息化升级项目,核心需求就是要做一个杜绝代签、高效便捷的考勤系统。经过一番技术选型和方案对比,最终我们敲定并实现了一套“基于安卓Android Studio和百度云的人脸识别学生考勤签到系统”。这个项目听起来技术栈挺全,涉及移动端开发、云服务、人工智能和数据库,但实际上,当你把每个模块拆解开,会发现它的实现思路非常清晰,而且用到的都是当前非常成熟、易获取的技术方案。

简单来说,这个系统就是一个安装在学生安卓手机上的App。学生上课前,打开App,调用手机前置摄像头进行人脸识别,识别通过后,考勤数据(学生ID、时间、地点)会通过百度云的相关服务(我们用的是百度人脸识别API和对象存储BOS)上传到云端服务器,服务器端再将这些数据写入MySQL数据库。管理员或老师则可以通过一个Web后台,实时查看、统计和导出考勤报表。整个流程下来,从学生端采集到后台管理,形成了一个完整的闭环。对于有Java/Android基础和Web开发经验的朋友来说,这个项目是一个绝佳的练手机会,它能让你把前后端、云服务、AI能力串联起来,做出一个真正可用的、解决实际痛点的产品。接下来,我就把这个项目的设计思路、关键技术选型、具体实现步骤以及我踩过的那些“坑”,毫无保留地分享给大家。

2. 系统整体架构与设计思路拆解

2.1 为什么选择“Android + 百度云 + 人脸识别”这个组合?

在做技术选型时,我们主要考虑了四个维度:成本、精度、开发效率和可维护性

首先,客户端为什么是Android App而不是微信小程序或Web页面?核心原因在于我们需要稳定、可控地调用手机摄像头进行活体检测。虽然微信小程序也有人脸识别API,但其调用流程受微信环境限制,且网络状况不稳定时体验较差。原生Android App可以更精细地控制摄像头参数(如对焦、分辨率)、实现更复杂的本地预处理(如图像压缩、亮度增强),并且在无网或弱网环境下可以先本地保存数据,待网络恢复后上传,鲁棒性更强。Android Studio作为官方IDE,其开发环境成熟,调试工具丰富,是移动端开发的不二之选。

其次,为什么选择百度云的人脸识别服务,而不是自研算法或使用其他云?这是一个关于“造轮子”还是“用轮子”的经典问题。自研人脸识别算法,需要庞大的标注数据集、深厚的AI算法功底和长期的模型调优,这对于大多数开发团队来说成本极高。百度云的人脸识别API提供了非常完善的解决方案:它包含了人脸检测、人脸比对、活体检测(防止用照片或视频欺骗)等一系列能力,并且精度高、响应快。更重要的是,它提供了丰富的免费额度,对于中小型项目初期完全够用,极大地降低了项目的启动门槛和后期运维成本。相比其他云服务商,百度云在人脸识别领域的API文档和SDK对中文开发者非常友好,社区资源也丰富,遇到问题更容易找到解决方案。

最后,数据流转与存储设计。考勤数据具有“写多读少”的特点,并且要求记录不可篡改。我们采用经典的“客户端 -> 云API -> 应用服务器 -> 数据库”架构。百度云对象存储BOS用来存放学生注册时上传的人脸底图,而每次签到产生的一条条记录,则通过应用服务器写入MySQL数据库。MySQL关系型数据库非常适合存储这种结构化的考勤流水,便于后续做复杂的SQL查询和统计分析。整个架构清晰,各司其职。

2.2 系统核心模块与工作流程

整个系统可以划分为四大模块:

  1. Android客户端 (App):负责学生身份验证、人脸图像采集、活体检测调用、考勤数据打包与上传。
  2. 云服务层 (百度云)
    • 人脸识别服务:提供人脸搜索(1:N比对)、活体检测等核心AI能力。
    • 对象存储 BOS:存储学生人脸注册照(Face Gallery)。
    • 云服务器 CVM/应用服务器:部署我们自写的后端服务,用于接收App请求、调用百度云API、操作数据库。
  3. 后端服务 (Spring Boot):作为桥梁,处理业务逻辑。它接收App上传的签到请求,先去百度云人脸库搜索匹配学生ID,匹配成功后再将考勤记录写入数据库。
  4. 数据存储层 (MySQL):存储学生信息表、课程表、考勤记录表等核心数据。
  5. Web管理后台 (可选,如Vue+Element UI):供老师查询考勤情况。

一次完整的签到流程如下:

  1. 学生打开App,输入学号(或刷卡/NFC自动获取)。
  2. App启动摄像头,引导学生完成眨眼、摇头等活体动作(由百度云SDK支持)。
  3. 采集到合格的人脸图片后,App将其与学号、时间戳、GPS位置(可选)打包,通过HTTPS协议发送到我们的后端服务器。
  4. 后端服务器收到请求,首先提取图片特征,调用百度云人脸搜索接口,在指定的人脸库中查找最相似的用户。
  5. 百度云返回相似度最高的用户ID(即学号)及置信度分数。后端将返回的ID与客户端上传的学号进行比对,若一致且置信度高于阈值(如80分),则认为是本人。
  6. 验证通过后,后端服务器生成一条包含学生ID、课程ID、签到时间、签到状态(成功/失败)、IP地址等的记录,插入MySQL的attendance_record表。
  7. 后端将签到成功或失败的结果返回给Android App,App提示用户。
  8. 老师登录Web管理后台,可以按课程、日期、学生查询详细的考勤报表。

注意:步骤4和5中,我们采用了“客户端上传学号+云端人脸搜索复核”的双重验证机制。这比单纯依赖客户端声称的身份更安全,可以有效防止客户端被篡改后伪造签到请求。

3. Android客户端开发核心细节解析

3.1 开发环境搭建与百度云SDK集成

首先,确保你的Android Studio是最新稳定版。项目层面的build.gradle需要配置百度云SDK的Maven仓库。人脸识别SDK我们主要依赖baidu-aip-face,网络请求使用OkHttp3,图片处理可以用Glide

集成百度云SDK的关键步骤:

  1. 在百度AI开放平台创建应用,获取API KeySecret Key。这是调用所有服务的前提。
  2. 在项目的app/build.gradle文件的dependencies中添加SDK依赖:implementation 'com.baidu.aip:java-sdk:4.16.3'(版本号请以官方最新为准)。
  3. 进行网络权限、相机权限、存储权限的声明。Android 6.0以上还需要动态申请相机权限,这是必须的,否则无法打开摄像头。
  4. 初始化人脸识别客户端。这里有个大坑:初始化一定要放在子线程或使用异步方式,因为其中涉及网络校验,在主线程操作会引发ANR(应用无响应)。
// 示例:在Application或首个Activity中初始化 public class MyApp extends Application { @Override public void onCreate() { super.onCreate(); new Thread(() -> { // 在子线程初始化 FaceSDKManager.getInstance().init(this, “你的API_KEY”, “你的SECRET_KEY”); // 设置活体检测控制(推荐开启) FaceSDKManager.getInstance().setLivenessControl(true, true, true); // 依次为RGB、IR、Depth活体,根据设备支持情况选择 }).start(); } }

3.2 人脸采集与活体检测界面实现

这是用户体验的核心环节。我们设计了一个全屏的相机预览界面,使用CameraX库(Google推荐,比旧的Camera API简单很多)来管理摄像头生命周期。

实现要点:

  1. 预览界面:使用PreviewView组件显示相机画面。在画面中央绘制一个圆角矩形或椭圆形的“人脸框”,引导用户将面部置于框内。
  2. 图像采集策略:不要持续不断地进行人脸检测,那样会非常耗电和发热。我们采用“定时采样”或“事件触发”的方式。例如,当检测到人脸在框内且姿态正常(无过大偏转)时,开始倒计时3秒,然后自动抓拍一张高质量图片。这比手动点击按钮更流畅。
  3. 活体检测集成:百度云SDK提供了LivenessDetect方法。在抓拍到图片后,我们不是直接将图片上传,而是先调用本地活体检测(如果SDK支持)或直接上传图片到云端进行活体分析。为了提升体验,可以在UI上给出文字或动画提示,如“请眨眼”、“请缓慢摇头”。
  4. 图片预处理:上传前,务必对图片进行压缩和格式转换。相机原始图片可能高达几MB,直接上传费流量、速度慢。我们可以将图片缩放至宽度为1080px(足够人脸识别),质量压缩到80%,并转换为JPEG格式。这里推荐使用Bitmap.createScaledBitmapByteArrayOutputStream配合Bitmap.compress方法。
// 图片压缩示例 private byte[] compressImage(Bitmap bitmap) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 计算缩放比例,使长边不超过1080 int width = bitmap.getWidth(); int height = bitmap.getHeight(); float scale = Math.min(1080f / width, 1080f / height); int newWidth = (int) (width * scale); int newHeight = (int) (height * scale); Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true); // 压缩并转成字节数组 scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 80, baos); return baos.toByteArray(); }

3.3 网络请求与数据上传

考勤数据上传必须稳定、可靠。我们使用OkHttp配合Retrofit来构建网络层。

  1. 数据封装:创建一个AttendanceRequest数据类,包含studentId(学号)、imageBase64(经过Base64编码的人脸图片)、timestamp(时间戳)、latitudelongitude(经纬度,需动态获取定位权限)、courseCode(课程代码)等字段。
  2. 重试机制:移动网络不稳定是常态。必须为签到请求添加重试逻辑。可以使用OkHttp的拦截器实现,当遇到网络超时或5xx服务器错误时,自动重试1-2次。
  3. 离线缓存:极端情况下,如果网络完全不可用,本次签到记录应该能缓存在本地(例如用Room数据库或SharedPreferences存储一条待上传记录),并在App下次启动或检测到网络恢复时,自动尝试上传。这是提升系统鲁棒性的关键。
  4. 安全传输:所有请求必须使用HTTPS。敏感信息如学号不要明文传输,可以和服务器约定一个简单的对称加密(如AES),或者使用非对称加密传输一个临时Token。至少也要对请求体做一次MD5签名,防止数据在传输中被篡改。

实操心得:在调试上传功能时,务必使用抓包工具(如Charles)查看实际发出的请求体和服务器返回。很多问题,比如字段名不对、编码格式错误(服务器端期望JSON但客户端发了Form-Data),通过抓包一目了然。另外,记得在后端对imageBase64字段的大小进行限制,防止客户端上传超大图片导致服务器内存溢出。

4. 后端服务与百度云API对接

4.1 Spring Boot后端项目搭建

我们选用Spring Boot来快速构建后端服务,它内嵌Tomcat,简化配置,非常适合作为此类系统的API服务器。

核心依赖(pom.xml):

<dependencies> <!-- Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 数据库 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- 百度云AI SDK --> <dependency> <groupId>com.baidu.aip</groupId> <artifactId>java-sdk</artifactId> <version>4.16.3</version> </dependency> <!-- 工具类 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>

核心配置(application.yml):

server: port: 8080 spring: datasource: url: jdbc:mysql://your-db-host:3306/attendance_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: your-username password: your-password driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update # 项目初期可以用update自动建表,生产环境务必改为validate或none show-sql: true baidu: aip: app-id: your-app-id api-key: your-api-key secret-key: your-secret-key

4.2 百度云人脸识别API的封装与调用

在后端,我们需要创建一个服务类来封装百度云人脸识别的操作。主要用到两个功能:人脸注册人脸搜索

  1. 人脸注册:当学生在系统首次录入信息时,需要将其人脸图片添加到百度云的人脸库中。一个最佳实践是,为每个班级或年级创建一个独立的Face Group,然后在Group下为每个学生创建一个Face User,以学号作为UserId。这样便于管理和分库查询,提升搜索效率。
@Service public class BaiduFaceService { private AipFace client; @PostConstruct public void init() { client = new AipFace(appId, apiKey, secretKey); // 可选:设置网络连接参数 client.setConnectionTimeoutInMillis(2000); client.setSocketTimeoutInMillis(60000); } /** * 注册人脸到指定库 * @param userId 学号 * @param groupId 班级ID,如"class_2023_01" * @param imageBase64 人脸图片Base64 * @return 是否成功 */ public boolean faceRegister(String userId, String groupId, String imageBase64) { HashMap<String, String> options = new HashMap<>(); options.put("user_info", ""); // 可以存一些额外信息,如姓名 options.put("quality_control", "NORMAL"); // 质量控制 options.put("liveness_control", "LOW"); // 活体控制 JSONObject res = client.addUser(imageBase64, "BASE64", groupId, userId, options); Integer errorCode = res.getInt("error_code"); if (errorCode != null && errorCode.equals(0)) { log.info("人脸注册成功: userId={}", userId); return true; } else { log.error("人脸注册失败: {}", res.toString()); return false; } } }
  1. 人脸搜索(1:N比对):这是签到环节的核心。收到App上传的图片后,在后端调用search接口,在指定的Group中进行查找。
/** * 在指定人脸库中搜索最相似的人脸 * @param imageBase64 待搜索图片 * @param groupIdList 要搜索的库列表,如["class_2023_01", "class_2023_02"] * @return 匹配到的用户ID(学号),若未匹配到或置信度低则返回null */ public String faceSearch(String imageBase64, List<String> groupIdList) { HashMap<String, String> options = new HashMap<>(); options.put("quality_control", "NORMAL"); options.put("liveness_control", "LOW"); options.put("max_user_num", "1"); // 返回最相似的1个用户 options.put("match_threshold", "80"); // 置信度阈值,80分 JSONObject res = client.search(imageBase64, "BASE64", groupIdList, options); if (res.getInt("error_code") == 0) { JSONObject result = res.getJSONObject("result"); if (result != null) { JSONArray userList = result.getJSONArray("user_list"); if (userList != null && userList.length() > 0) { JSONObject user = userList.getJSONObject(0); double score = user.getDouble("score"); if (score >= 80) { // 再次确认阈值 return user.getString("user_id"); // 返回学号 } } } } log.warn("人脸搜索未找到匹配项或分数过低: {}", res.toString()); return null; }

注意事项:百度云人脸搜索API是按调用次数计费的,并且QPS(每秒查询率)有限制。在代码中一定要做好限流和降级。例如,可以使用Guava的RateLimiter对签到接口做限流,防止恶意刷接口。同时,当百度云API不可用时,应能快速失败或切换到备用方案(如记录日志,人工补签),避免整个系统卡死。

4.3 签到业务逻辑与数据库设计

后端提供一个/api/attendance/check-in的POST接口,用于处理签到请求。

核心业务逻辑代码片段:

@PostMapping("/check-in") public ApiResult checkIn(@RequestBody AttendanceRequest request) { // 1. 参数基础校验 if (StringUtils.isEmpty(request.getStudentId()) || StringUtils.isEmpty(request.getImageBase64())) { return ApiResult.fail("参数缺失"); } // 2. 调用百度云人脸搜索API,在对应的班级人脸库中查找 String matchedUserId = baiduFaceService.faceSearch(request.getImageBase64(), getGroupListByStudent(request.getStudentId())); // 3. 验证匹配结果 if (matchedUserId == null || !matchedUserId.equals(request.getStudentId())) { // 人脸识别失败:可能是非本人、照片质量差、不在库中 saveAttendanceRecord(request.getStudentId(), “FAIL”, “FACE_MISMATCH”); return ApiResult.fail("人脸识别失败,请重试或联系管理员"); } // 4. 人脸识别成功,检查是否重复签到(同一课程规定时间内) if (isAlreadyCheckedIn(request.getStudentId(), request.getCourseCode())) { return ApiResult.fail("您已签到,请勿重复操作"); } // 5. 生成并保存成功的考勤记录 AttendanceRecord record = saveAttendanceRecord(request.getStudentId(), “SUCCESS”, null); // 6. 返回成功响应 return ApiResult.ok("签到成功", record); }

数据库表结构设计(关键表):

  • 学生表 (student)

    字段名类型说明
    idbigint主键
    student_novarchar(20)学号,唯一
    namevarchar(50)姓名
    class_idvarchar(20)所属班级ID,关联人脸库GroupId
  • 考勤记录表 (attendance_record)

    字段名类型说明
    idbigint主键
    student_idbigint学生ID,外键
    course_idbigint课程ID
    check_in_timedatetime签到时间
    statusvarchar(10)状态:SUCCESS/FAIL
    fail_reasonvarchar(100)失败原因(如 FACE_MISMATCH, TIMEOUT)
    locationvarchar(255)签到位置(GPS或IP解析)
    ip_addressvarchar(45)客户端IP
  • 课程表 (course) 和 班级表 (class)根据实际业务需求设计。

实操心得:在attendance_record表中,我强烈建议除了status字段,一定要加一个fail_reason字段。这在后期排查问题、分析签到失败原因(是网络超时、人脸不匹配,还是时间不对)时,价值巨大。另外,签到时间check_in_time务必使用服务器时间,而不是依赖客户端上传的时间戳,以防止客户端时间不准或被篡改。

5. 数据库部署与SQL文件说明

项目提供的sql文件通常包含了创建数据库、数据表以及插入初始测试数据的语句。这是项目能跑起来的基础。

5.1 SQL文件结构与执行顺序

一个完整的SQL文件可能包含以下部分,执行时必须按顺序进行

  1. 创建数据库CREATE DATABASE IF NOT EXISTS attendance_db DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;使用utf8mb4字符集是为了支持存储Emoji等特殊字符(虽然考勤系统可能用不到,但这是个好习惯)。
  2. 选择数据库USE attendance_db;
  3. 删除旧表(可选,用于初始化)DROP TABLE IF EXISTS attendance_record, student, course, ...;注意表之间的外键依赖关系,要先删除子表(如attendance_record),再删除父表(如student)。
  4. 创建表:按照设计好的表结构,依次创建。这里要特别注意外键约束索引
    • 外键attendance_record表中的student_id字段应关联student表的id。这能保证数据的一致性,但有时在高并发写入的场景下,外键约束可能会影响性能,需要根据实际情况权衡。
    • 索引:这是优化查询性能的关键。至少应该在以下字段上创建索引:
      • attendance_record(student_id, check_in_time):复合索引,用于快速查询某个学生的历史考勤。
      • attendance_record(course_id, check_in_time):用于按课程统计考勤。
      • student(student_no):学号是唯一查询条件,必须建唯一索引。
  5. 插入初始数据:插入一些测试用的学生、课程信息,方便开发阶段调试。

5.2 生产环境数据库优化建议

当系统真正上线,数据量增长后,以下几点优化至关重要:

  1. 分表分区:考勤记录表attendance_record会随着时间急剧增长。可以考虑按时间范围进行分区(Partitioning),例如按月或按学期分区。这样在查询某个时间段的数据时,数据库只需扫描特定分区,速度会快很多。对于历史久远的数据,也可以方便地归档或清理。
  2. 读写分离:如果Web后台的报表查询非常频繁且复杂,可以考虑做主从复制(Master-Slave Replication)。写操作(签到插入)在主库进行,读操作(查询报表)在从库进行,减轻主库压力。
  3. 慢查询监控:务必开启MySQL的慢查询日志,定期分析执行时间过长的SQL语句,并针对性地进行优化(如调整索引、重写SQL)。
  4. 连接池配置:在Spring Boot的application.yml中,合理配置数据库连接池(如HikariCP)的参数,如maximum-pool-size(最大连接数)、connection-timeout(连接超时)等,避免因连接数不足导致请求堆积。

6. 常见问题与排查技巧实录

在实际开发和部署过程中,我遇到了不少典型问题,这里整理出来,希望能帮你提前避坑。

6.1 Android客户端常见问题

问题1:人脸识别成功率低,尤其在光线暗或侧脸时。

  • 排查:首先检查采集的图片质量。在调用百度云API前,可以把Base64图片解码保存到手机相册,看看实际上传的图片是否模糊、过暗或过曝。
  • 解决
    • 优化采集环节:在相机预览时,通过CameraCharacteristics获取设备支持的最高质量,并设置合适的对焦模式(如CONTROL_AF_MODE_CONTINUOUS_PICTURE)。可以尝试在界面上增加一个“手动补光”的按钮,调高屏幕亮度或打开闪光灯(如果支持)。
    • 本地预处理:在图片上传前,使用android.graphics.ColorMatrix进行简单的亮度、对比度调整。也可以尝试一些轻量级的图像锐化算法。
    • 调整API参数:调用百度云searchaddUser接口时,将quality_control参数设置为HIGH,这样API会主动拒绝质量太差的图片,并返回错误码222202(图片质量不合格),此时可以提示用户“图片质量不佳,请调整光线重试”。

问题2:App在部分机型上崩溃,日志显示UnsatisfiedLinkError

  • 排查:这通常是百度云人脸识别SDK的so库(C++动态库)与手机CPU架构不兼容导致的。百度云SDK一般会提供armeabi-v7a,arm64-v8a,x86等架构的库。
  • 解决:在app/build.gradleandroid->defaultConfig块中,使用ndk配置指定需要打包的ABI,只打包你需要的,可以减小APK体积。
    android { defaultConfig { ndk { abiFilters 'armeabi-v7a', 'arm64-v8a' // 目前市面上绝大多数手机是这两种架构 } } }
    同时,检查是否在src/main/jniLibs目录下放入了正确的so库文件。

6.2 后端服务与API对接问题

问题3:签到接口偶尔超时,导致学生端等待很久。

  • 排查:使用链路追踪工具(如SkyWalking)或详细的日志,定位耗时环节。通常瓶颈在于网络I/O:一是App到后端服务器的网络,二是后端服务器到百度云API的网络。
  • 解决
    • 设置超时与重试:在调用百度云SDK的AipFace客户端时,务必设置合理的连接超时和读写超时(如上面代码示例)。同时,在后端调用百度云API的代码外层,添加一个带有退避策略的重试机制(例如,最多重试2次,每次间隔1秒)。
    • 异步处理:对于签到请求,可以将其改为异步流程。收到请求后,立即返回“正在处理中”的响应,然后将人脸识别和数据库写入操作放入消息队列(如RabbitMQ、Kafka)或线程池中异步执行。最后通过WebSocket或App轮询告知用户结果。这能极大提升接口的响应速度。
    • 服务部署:确保你的后端服务器和百度云API服务在同一个地域(例如都在华北-北京),以减少网络延迟。

问题4:百度云API返回错误码222207(人脸不在库中),但学生明明已注册。

  • 排查
    1. 检查注册和搜索时使用的groupId是否完全一致,包括大小写。
    2. 检查注册时的人脸图片和学生签到时的姿态、表情、戴眼镜等情况是否差异过大。
    3. 登录百度云控制台,查看对应人脸库的用户列表,确认该学生是否注册成功。
  • 解决
    • 标准化注册流程:要求学生注册时在光线均匀的正面环境下,拍摄不带夸张表情、不戴帽子口罩的照片。可以强制要求进行“眨眼”活体检测,确保是真人。
    • 多张底图:百度云支持一个用户添加多张人脸图片。可以在学生注册时,采集2-3张不同角度(轻微左右偏转)或不同表情的图片作为底图,提升识别率。
    • 定期更新底图:对于成长期的学生,可以每学期或每学年重新采集一次人脸底图。

6.3 数据库与性能问题

问题5:考勤记录表数据量百万级后,按学生查询历史记录变慢。

  • 排查:使用EXPLAIN命令分析慢查询SQL。很可能是因为where student_id = xxx这个条件没有用到索引,或者索引失效。
  • 解决
    • 确认索引:确保attendance_record表在student_idcheck_in_time上建立了复合索引idx_student_time
    • 避免索引失效:在查询时,避免对student_id字段进行函数操作(如WHERE CAST(student_id AS CHAR) = '123'),也不要使用OR连接多个条件(除非每个条件都有索引)。尽量使用覆盖索引,只查询索引包含的字段。
    • 分页优化:对于“查询某学生最近100条记录”这种需求,不要用LIMIT 100000, 100这种深度分页,效率极低。可以记录上一页最后一条记录的idcheck_in_time,然后使用WHERE student_id = xxx AND id < last_max_id ORDER BY id DESC LIMIT 100来查询。

问题6:在高并发签到时段(如上课前5分钟),数据库连接池被占满,出现Cannot get connection异常。

  • 排查:监控数据库连接数和使用情况。检查应用日志,看是否有慢SQL阻塞了连接释放。
  • 解决
    • 优化慢SQL:如上所述,解决慢查询是根本。
    • 调整连接池参数:适当调大maximum-pool-size,但不要设置过大(一般不超过CPU核心数 * 2 + 磁盘数)。同时,合理设置connection-timeout(获取连接的超时时间)和idle-timeout(连接空闲超时)。
    • 引入缓存:对于不经常变动的数据,如课程信息、学生基本信息,可以引入Redis等缓存。在签到逻辑中,先查缓存,缓存没有再查数据库,并回写到缓存。这能显著减少数据库的读压力。
    • 业务降级:在极端高并发下,可以考虑暂时将人脸识别成功的记录先写入一个高速的中间件(如Redis List或Kafka),然后由后台任务异步批量写入数据库,实现“最终一致性”。但这会牺牲一定的实时性,需要根据业务容忍度来权衡。

这套系统从零到一搭建起来,涉及的知识点确实不少,但每一步都有迹可循。我的体会是,关键在于把复杂的系统拆解成一个个独立的模块,先让每个模块单独跑通,然后再像拼乐高一样把它们组合起来。过程中遇到问题,善用日志、抓包工具和官方文档,大部分都能找到答案。最后,别忘了在正式上线前,做好充分的压力测试和安全评估,比如模拟几百个学生同时签到,检查服务器的CPU、内存、数据库连接数是否在健康范围内。希望这份详细的复盘,能为你实现自己的考勤系统提供一份可靠的“地图”。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询