公司动态

基于Android的大学生课堂考勤系统:从设计到部署全解析

📅 2026/8/31 5:54:46
基于Android的大学生课堂考勤系统:从设计到部署全解析
简介本资源是一套面向高校教师与计算机专业学生的Android课堂考勤系统完整开发包聚焦解决传统点名易代答、统计难、反馈滞后等教学管理痛点。系统采用Android客户端Web服务端双入口设计支持人脸识别级照片核验防代考实现考勤实时上传、班主任动态查看、期末自动统计等功能适用于智慧校园信息化建设与课程设计实践。压缩包共403个文件含58张学生照片jpg/png、27个核心Java业务逻辑文件、89个编译后class字节码、25个Android布局与配置xml、24个PHP服务端接口脚本、14个前端交互js及配套css、1个可直接安装的apk应用另有SQL建表语句、数据库备份与多份配置备份文件.bak整体体积仅5.65MB结构清晰便于二次开发与部署。目前已有674人学习下载提供从客户端到服务端再到数据库的全链路源码与文档是Android移动开发与Web后端集成的典型教学案例。 课堂考勤这件事做过班委或者给老师当过助手的同学应该都有感触。每到上课前几分钟教室里总会乱成一团点名册传来传去代签、漏签、补签的情况层出不穷。老师站在讲台上数人头台下学生低头玩手机这种场景几乎每所大学都在上演。我做过一版基于Android的大学生课堂考勤系统里面包括Android客户端源码、服务端源码、数据库设计文档和整套说明文档前后大概花了两周时间完成属于典型的课程设计或者毕业设计级别项目。这套系统不是停留在演示层面的玩具它把定位签到、二维码扫码、请假审批、统计报表这些功能完整串了起来从手机端到后端服务再到数据库落库链路是通的。如果你正在纠结考勤系统怎么做或者想找一套能直接拿来改改用的完整源码项目这篇文章会把这套系统的脉络、关键代码思路、数据库设计逻辑和部署坑位全部拆开来讲。先说清楚这套系统解决的核心问题传统课堂考勤靠纸质点名或者口头答到效率低、容易作弊、数据难统计。基于Android的考勤系统本质是把“老师点名”这件事数字化让签到动作发生在学生自己的手机上服务端负责记录和判定数据库负责持久化。这样老师打开后台就能看到出勤率、迟到名单、请假记录学生打开手机App就能一键签到管理员还能导出统计报表。整体链路不复杂但涉及的知识点覆盖了Android界面开发、网络请求、定位服务、二维码扫描、服务端接口设计、数据库表关系设计等刚好是一套标准的全栈练手项目。这套系统的受众其实很明确一是计算机相关专业正在做课程设计或毕业设计的在校生二是想从前端或者单纯Android方向往全栈靠一靠的开发者。你不需要有多深的架构经验只要会Java基础、了解Android基本组件再稍微懂点SQL和Spring框架就能跟着源码把这套系统跑起来。下面我按模块拆开讲每个部分都会结合我实际开发时踩过的坑和调整过的方案来说。1. 项目整体设计与思路拆解1.1 核心需求与功能定位做任何项目之前先把需求边界划清楚不然做到一半就会失控。这套考勤系统的核心用户有三类学生、教师、系统管理员。学生的核心操作是查看课程表、发起签到、提交请假教师的核心操作是创建课程、发起考勤、查看签到统计、审批请假管理员则负责基础数据维护比如学生信息、教师信息、班级信息、课程信息的增删改查。基于这些需求系统整体分为三个端Android客户端学生和教师共用通过角色区分权限、服务端RESTful API、数据库MySQL。没有单独做Web管理端教师端的部分功能通过Android客户端完成管理员的批量数据维护则直接操作数据库或者附加一个简单管理页面。这个取舍比较符合课程设计的工作量也避免了战线拉太长导致做不完的问题。实际开发时需求边界一定要写清楚并和指导老师确认。我第一版做的时候野心很大想把视频签到、人脸识别、蓝牙定位全塞进去结果发现工作量根本收不住。后来收敛到定位签到扫码签到请假审批统计导出这四个核心功能项目才真正落地。这算是一条很实用的经验毕设或课设项目优先保证核心链路完整而不是追求功能数量。1.2 技术选型与架构分层技术选型决定了下半程开发是轻松还是痛苦。客户端我选了纯Java Android原生开发没有用Kotlin和Flutter原因很简单课程设计阶段绝大多数学校的教材和实验环境还是Java为主改用Kotlin虽然写起来更简洁但会引入额外的学习成本和兼容性排查成本。网络层使用OkHttp Gson没有引入RxJava和Retrofit全家桶保证源码读起来直观老师答辩问起来你也能讲清楚每一个环节。服务端选型上Spring Boot 2.x是最稳的选择。它内嵌了Tomcat减少了大量xml配置跑起来快资料也多。持久层用的是MyBatis没有用Spring Data JPA因为MyBatis的SQL可读性更强数据库课上学的那些联表查询都能直接写出来答辩时也能顺势展示SQL功底。数据库用MySQL 5.7这是国内课设环境里最普及的版本8.0也兼容但一些驱动和连接配置会稍有差异。架构分层遵循标准的三层结构Android客户端只负责界面展示和用户交互通过HTTP协议调用服务端接口服务端按Controller、Service、Mapper分为三层统一处理业务逻辑和鉴权数据库只负责数据存取。三层结构的好处是职责分离、定位问题快客户端出问题查客户端服务端接口报错查服务端不用上下游来回猜。提示如果你打算基于这套源码二次开发建议保持三层结构不动只替换具体业务逻辑。我见过不少同学为了图省事把SQL直接写在Controller里项目是跑通了但答辩被老师一追问就卡壳了。2. Android客户端核心模块解析2.1 客户端工程结构与页面规划Android客户端的包名规划直接暴露了一个开发者对项目的理解是否清晰。这套系统的客户端工程目录划分比较标准activity包存放页面adapter包存放列表适配器entity包存放实体类network包封装网络请求utils包放工具类service包放后台服务。四个核心页面分别是登录页、首页课程列表、签到页定位/扫码、个人中心考勤记录与请假。登录页承担的是角色分发学生登录后默认进入课程列表页教师登录后进入考勤管理页。这里用了一个技巧——登录接口返回的JSON里包含role字段客户端拿到后直接缓存到SharedPreferences之后每次跳转前都检查一下角色。界面层面没有做复杂的动画和花哨的UI重点保证逻辑通顺、控件命名规范。实际开发中页面规划的先后顺序很重要。我的做法是先把登录和课程列表跑通再往后加签到和请假流程因为这两个页面是整个App的地基地基不稳后面全白搭。规划页面时强烈建议先画一遍界面草图哪怕是用纸笔把每个按钮点击后跳转到哪里、每个列表项点击后做什么都标记清楚代码写起来会顺很多。2.2 网络请求封装与会话保持客户端和服务端的交互全部走HTTP接口。这里不推荐直接在Activity里new OkHttpClient然后发请求代码会冗余到没法看。我封装了一个HttpUtil工具类统一处理GET、POST请求、JSON解析和错误码提示。核心思路是所有接口走同一个入口请求参数用HashMap传递返回结果统一用JsonObject包装里面包含code、message、data三个字段。会话保持用Token机制。用户登录成功后服务端返回一个Token字符串客户端把它缓存到SharedPreferences里后续每次请求都在Header里带上Authorization字段。服务端通过拦截器校验Token校验通过才放行。这套机制实现简单也能满足移动端的身份认证需求。答辩的时候如果老师问“为什么用Token而不用Session”你就回答移动端不像浏览器有Cookie自动管理机制Token更轻量、跨端更友好。关于网络请求的线程问题踩过不少坑。Android要求网络请求不能在主线程执行否则会抛NetworkOnMainThreadException。所以封装时必须用子线程发起请求拿到结果后再通过runOnUiThread切回主线程更新UI。更规范的方案是用Handler或者RxJava但对于课设来说runOnUiThread已经够用。2.3 签到模块的两种实现方式签到是这套系统的灵魂功能。我实现了两种签到方式GPS定位签到和二维码扫码签到。GPS定位签到适合老师要求学生在教室现场签到的情况。原理很简单客户端通过LocationManager获取经纬度上传给服务端服务端和课程设置的签到地点经纬度做距离计算距离小于设定阈值比如100米判定为有效签到。这个方案实现成本低但有个明显的坑——室内环境GPS信号弱定位偏差大。二维码扫码签到解决的是“人不在现场但能定位”的问题。老师端打开课程后生成一个二维码里面包含课程ID和当前时间戳学生用App内置的扫码功能扫描二维码服务端解析二维码内容并校验课程信息和时间段校验通过就记为有效签到。二维码用zxing库实现这个库虽然已经好多年没更新了但在离线扫码场景下依然稳定可靠课程设计用完全没有问题。两种签到方式我建议都保留因为不同课程的签到场景不一样。教室密集的实验楼里GPS定位飘得离谱二维码扫码更可靠而体育馆、操场这类室外场景没有大屏幕展示二维码GPS定位反而是更好的选择。代码层面两种签到共用同一个签到成功页只是数据来源不同这样UI逻辑可以复用。注意定位签到一定要处理权限请求。Android 6.0以上动态权限是绕不开的坑除了在AndroidManifest.xml里声明权限还要在代码里用requestPermissions动态申请否则在Android 9以上的设备上直接崩溃。这是我改了三次才彻底解决的问题。2.4 请假与考勤记录的实现细节请假功能说起来简单做起来也有不少细节。学生端提交请假申请填写请假类型事假、病假、开始时间、结束时间、请假原因然后提交给服务端。服务端把请假记录写入leave_apply表状态初始为0待审批。教师端能看到待审批的请假列表点击审批后状态变为1通过或2驳回。考勤记录页面是一个列表展示学生每一门课程的到课情况。这里服务端做了一个联表查询把course、attendance_record和学生信息关联起来返回给客户端一个带有课程名、签到时间、签到状态的对象列表。状态用数字表示0未签到、1正常、2迟到、3请假、4缺勤。迟到判定由服务端计算签到时间晚于课程开始时间10分钟则标记为迟到超过30分钟直接标记为缺勤。这些状态在客户端展示时映射成对应的中文文本和颜色标识列表项的视觉区分非常清晰。3. 服务端接口与数据库设计3.1 服务端框架与项目构建服务端基于Spring Boot搭建目前依赖了Web、MyBatis、MySQL驱动、Lombok这几个核心组件。项目结构按标准的分层模式来写controller包接收前端请求service包处理业务逻辑mapper包接口配合resources下的xml文件操作数据库entity包对应数据表实体。构建工具使用Mavenpom.xml里配置了阿里云镜像仓库国内开发环境下下载依赖速度快很多。如果你想把这套服务端源码在自己电脑上跑起来第一步就是确保Maven的settings.xml配置了合适的镜像源否则依赖下载会非常痛苦。服务端启动端口默认配置在8080可以通过application.yml修改。数据库连接信息同样在application.yml中配置包括URL、用户名和密码。这里要提醒一点不同学校机房MySQL的安装方式不一样密码设置也千奇百怪最容易出的问题就是连接串里的密码和本地数据库不一致。调试的时候先确认服务端日志里有没有报数据库连接异常通过日志带动排查是最快的方式。3.2 数据库表结构设计详解数据库是整个系统最核心的部分表结构设计得好不好直接决定了后续功能能不能顺畅扩展。这套系统一共设计了6张核心表表名用途关键字段user用户表id, username, password, real_name, role, student_no, create_timecourse课程表id, course_name, teacher_id, course_time, location, latitude, longitude, sign_start_time, sign_end_timecourse_student学生选课表id, course_id, student_idattendance_record考勤记录表id, course_id, student_id, sign_time, status, sign_typeleave_apply请假申请表id, student_id, course_id, reason, start_time, end_time, status, approver_idcourse_code签到码表id, course_id, code, expire_timeuser表是整个系统的基石所有角色都在这张表里通过role字段区分。role为1表示管理员2表示教师3表示学生。student_no字段存储学号教师账号也用这个字段存工号只是语义上不同。密码字段存储的是MD5加密后的密文不是明文。这一点答辩时候经常会问答得上来说明了解基本的密码安全常识。course_student表是学生和课程的多对多关联表。课程可以被多个学生选择一个学生也可以选择多门课程这种多对多关系在关系型数据库里必须拆成中间表。如果没有这张表后续做考勤记录和统计报表都会非常麻烦。attendance_record表记录每一次签到的结果status字段的值域和客户端展示的逻辑保持一致。3.3 接口设计与核心接口清单接口设计遵循RESTful风格但不过度追求纯REST因为课程设计阶段往往不需要那么严格的资源语义只要路径清晰、职责明确就行。核心接口有以下几组POST /api/auth/login登录接口参数为username和password返回用户信息和TokenGET /api/course/list获取当前用户的课程列表学生返回已选课程老师返回自己创建的课程GET /api/course/detail获取课程详情包括签到时间、地点、经纬度信息POST /api/sign/gpsGPS定位签到参数为courseId、latitude、longitudePOST /api/sign/scan扫码签到参数为二维码内容POST /api/leave/apply学生提交请假申请GET /api/leave/list获取请假记录列表学生看自己的老师看待审批的POST /api/leave/approve教师审批请假所有接口统一返回JSON格式包含code、message、data三个字段。code为200表示成功401表示未授权500表示服务端异常。客户端HttpUtil统一解析这个结构根据code决定是否弹出错误提示。接口定义好之后我建议用Postman或Apifox先跑一遍服务端接口确认通了再动客户端联调。很多人的痛苦在于客户端一联调就报错结果定位了半天发现是服务端接口本身还没写对浪费了大量时间。先单独验证服务端再前后端联调这个顺序能省下至少50%的联调时间。3.4 Token鉴权与数据安全Token鉴权的实现思路是登录成功后服务端生成一个UUID字符串作为Token把Token和用户ID的映射关系存到内存Map中同时设置过期时间。每个需要鉴权的请求都通过拦截器读取Header里的Authorization字段去Map中查找对应的用户信息找不到就返回401。这种方式对小规模系统来说简单够用。大规模生产环境会引入Redis存Token并考虑分布式问题但对于课堂考勤这种并发量很小的场景内存Map已经足够。答辩的时候说到这一层老师就知道你理解了中间件在系统中的定位。密码安全方面我在代码里看到了一个细节登录接口接收到密码后先做MD5加密再和数据库里存的密文比对。虽然MD5在现代安全标准中不够强但对于校园局域网环境下的课程设计项目MD5加盐已经是合格水平。如果想让项目显得更专业可以把MD5升级为BCrypt加密Spring Security的BCryptPasswordEncoder类可以直接引用改动量不大。4. 核心功能流程的完整实现方案4.1 登录认证到课程列表的完整链路把登录到课程列表这条主链路串起来讲整个系统的运转逻辑就清晰了。客户端打开登录页用户输入学号和密码点击登录按钮后HttpUtil发起POST请求到/api/auth/login参数是用户名和密码。服务端Controller接收参数后调用Service层Service层查询数据库用户表校验密码校验通过生成Token返回。客户端拿到返回的JSON解析出用户信息、角色和Token把Token存到SharedPreferences然后根据角色跳转到对应页面。课程列表页加载时客户端先从本地读取Token和用户ID然后请求/api/course/list接口。服务端根据用户角色做不同处理如果是学生查询course_student表找到该学生所有选课记录再关联course表返回课程详情如果是教师直接查course表返回该教师创建的课程。数据返回到客户端后RecyclerView的Adapter把课程名称、上课时间、地点渲染到界面上。这条链路虽然看起来简单但它是整个系统的主动脉。登录、鉴权、数据库联表查询、列表渲染所有核心知识点都覆盖了。开发时我建议先走通这条主链路再做其他分支功能。主链路通了整个项目就有了骨架后续往里填模块会非常踏实。提示如果课程列表接口返回数据为空先别急着找客户端问题。用Navicat打开数据库看看course和course_student表里有没有造好的测试数据。很多同学项目跑起来页面空白最后发现是数据库里一条数据都没插入。4.2 定位签到和扫码签到的完整流程GPS定位签到的时序流程是学生点击签到按钮客户端定位获取当前经纬度把经纬度连同courseId一起发送给服务端。服务端读取course表里存储的课程经纬度用Haversine公式计算两点的球面距离距离小于100米判定为签到成功写入attendance_record表status设为1。如果距离超过阈值返回错误信息提示“不在签到范围内”。扫码签到的流程简单一些老师在教师端点击“生成签到码”按钮服务端生成一个唯一码并保存到course_code表同时设置过期时间比如30分钟有效最终以二维码形式展示在老师手机上。学生扫描二维码后客户端解析出码内容并发送到服务端服务端校验码是否存在、是否过期、是否属于当前这门课程校验通过后写入考勤记录。这里有个细节值得注意扫码签到时客户端需要把当前登录用户ID一起传给服务端服务端不能只靠二维码内容来判断是谁在签到。否则如果一个学生把二维码截图发给其他同学别人也能扫码成功这个漏洞在答辩时被老师抓住会很尴尬。我在实现时扫码签到的接口同时校验码的有效性和当前登录用户是否选修了这门课程两者都通过才算签到成功。4.3 考勤统计与报表导出的实现思路考勤统计是教师端最实用的功能。教师进入某门课程的统计页面服务端聚合该课程所有学生的签到记录返回每个学生的出勤次数、迟到次数、缺勤次数、请假次数。客户端用柱状图或者饼图展示汇总数据简单的图表可以用MPAndroidChart库界面效果不错引入成本低。如果想导出Excel报表可在服务端引入Apache POI库。服务端根据统计数据生成Excel文件并返回给客户端下载或者提供一个下载链接由浏览器直接访问。我实现的版本里教师端可以按课程导出整个班级的考勤明细表字段包括学号、姓名、签到时间、签到状态。这对老师来说是非常实用的功能答辩时也容易出彩。提示POI库的版本选择要注意和Java版本兼容。Spring Boot 2.x对应POI 4.x或5.x均可但POI 5.x要求Java 8以上如果你本地是Java 11或更高版本放心使用5.x。4.4 请假审批的流程闭环请假审批流程看起来不复杂但它是考勤系统闭环不可或缺的一环。没有请假功能学生缺勤只能一律记为缺勤这对有正当理由缺课的学生不公平老师也不好操作。我把请假流程设计成三步学生提交、教师审批、考勤状态更新。学生提交请假申请时客户端将学号、课程ID、请假类型、起止时间、理由作为参数传给服务端服务端插入leave_apply表状态为待审批。教师端刷新请假列表看到待审批的申请后点击通过或者驳回。如果通过服务端自动判断本次请假是否覆盖了该课程的上课时间如果覆盖则在attendance_record表中生成一条状态为3请假的记录保证考勤统计时该学生不会被记成缺勤。这里有个容易遗漏的环节学生可能一次请假跨多天、多节课程。实现时要做时间区间与课程时间的重叠判断不能只标记一门课程。我用一个两层循环实现的外层遍历请假时间段内的所有日期内层遍历该学生的所有课程判断课程上课时间是否落在请假区间内命中则生成对应的请假考勤记录。5. 环境搭建、部署配置与问题排查实录5.1 从零跑通整个项目的标准步骤拿到这套源码后从零开始跑通全项目需要按顺序操作。先说服务端环境准备安装JDK 1.8或11、Maven 3.6、MySQL 5.7或8.0、Navicat或MySQL Workbench。准备就绪后用Navicat创建一个名为attendance_system的数据库然后导入项目database目录下的SQL脚本。脚本会创建全部6张数据表并插入一部分测试数据包括几个学生账号、教师账号和课程记录。接着修改application.yml里的数据库用户名和密码确保和本地MySQL一致。用Maven执行mvn spring-boot:run或者在IDE里直接运行主类看到“Started ... Application”日志说明服务端启动成功。这时用Postman测试接口比如调用登录接口确认能返回Token。客户端部分用Android Studio打开AndroidClient目录。首次打开会自动下载依赖时间取决于网络和配置。先在build.gradle里确认compileSdkVersion和targetSdkVersion建议compileSdkVersion 30targetSdkVersion 30。然后在网络工具类里改服务端IP地址如果使用模拟器用10.0.2.2代指宿主机如果是真机要改成电脑在局域网中的IP比如192.168.1.100。改完后点击Run选择模拟器或真机运行。提示真机调试时手机和电脑必须处于同一WiFi网络防火墙也要放行8080端口。Windows系统开着防火墙大概率会拦截来自手机的访问开发时建议直接关闭防火墙或者在入站规则里放行Java进程。5.2 网络通信相关的高频报错与解法联调阶段最常遇到的网络报错我梳理成了一张速查表报错信息原因解决方案NetworkOnMainThreadException网络请求写在主线程用子线程或Handler包装请求Cleartext HTTP traffic not permittedAndroid 9默认禁止明文HTTP在Manifest的application节点加android:usesCleartextTraffictrueConnection refused服务端没启动或IP端口不对确认服务端启动成功确认IP和端口配置正确TimeoutException超时时间太短或网络不通检查网络把OkHttp的connectTimeout调大Bad Request接口请求参数格式不对用Postman先测通确认参数名和类型这里面ClearText HTTP报错是Android 9以上的新政策课堂上很多没讲到。如果你的targetSdkVersion大于28访问HTTP明文地址就会直接拒绝必须配置usesCleartextTraffic为true或者改用HTTPS。很多同学一联网就崩改了这个配置就好。5.3 数据库层面的典型坑点数据库层面的问题大多集中在SQL脚本导入失败和中文乱码两类。导入失败通常是因为脚本里的SQL语法版本和本地MySQL不兼容特别是存在DEFAULT CURRENT_TIMESTAMP这类语法时MySQL 5.x和8.x表现差异很微妙。解决方法是直接打开SQL文件复制关键建表语句在Navicat的查询窗口里逐段执行精确复现问题。中文乱码问题则出现在连接串没有指定characterEncoding参数的时候。MySQL连接URL的末尾一定要加上useUnicodetruecharacterEncodingutf8否则写入的中文会变成问号。注意MySQL 8.0的驱动类名改成了com.mysql.cj.jdbc.DriverURL里还建议加serverTimezoneAsia/Shanghai不然可能出现时区相关的报错。5.4 客户端运行与Android Studio的兼容问题Android Studio版本差异带来的坑也不少。如果你用的是较新的Android Studio版本打开老项目的时候Gradle同步可能会失败。常见的错误是Gradle版本和插件版本不匹配或者依赖仓库访问不到。我建议做一次极简处理新建一个空白项目复制模块代码或者直接升级项目的Gradle版本到当前Android Studio默认推荐的版本。还有一个高频问题是模拟器不显示定位结果。Android模拟器默认位置在美国旧金山需要在模拟器的Extended Controls里手动设置经纬度坐标模拟定位否则定位签到永远显示不在范围内。这个坑位解答了很多学生问的“为什么我在模拟器上定位签到总是失败”的问题。真机调试则要确保定位权限和GPS开关都已打开。6. 项目改进方向与扩展思路基础功能说完说说这套系统还能往哪些方向扩展。课程设计的评分往往看重创新点在原系统上加一两个亮点答辩分数会明显不一样。一个可行的方向是加入WiFi指纹定位辅助签到。教学楼里GPS信号差但每个教室的WiFi信号覆盖是稳定的。可以在服务端预先采集每个教室WiFi信号的特征值签到时客户端上报当前扫描到的WiFi列表和信号强度服务端和指纹库匹配落在对应教室则签到成功。这个方案比GPS更精准也更有技术含量但实现复杂度高一些。另一个方向是地理围栏Geofencing。Android系统原生支持Geofencing API可以在地图上画一个半径100米的圆形区域学生进入区域后系统自动触发签到提醒不用手动点击签到按钮。这个功能非常适合超大课时的课堂展示效果也好。需要引入Google Play Services库真机调试限制较多模拟器基本不能用。从技术栈升级的角度客户端可以逐步迁移到Kotlin和Jetpack Compose服务端可以把内存版Token换成Redis管理数据库表可以引入分表分库策略。但这些方向对课设来说属于锦上添花先把现有代码吃透能在答辩时讲清楚每一行关键代码的逻辑其实比堆功能重要的多。最后再分享一点个人体会。这个项目做完我对“全栈开发”这个概念有了实感——单纯的Android页面展示很容易学但当你把数据从数据库经过服务端接口送到界面上的时候整条链路的每一环都会因为一个字段名不一致、一个时间格式不统一而卡住。课程设计的意义不在于功能多炫而在于你能完整地掌控从数据库表设计到前端展示的全过程。我这套考勤系统源码里的注释写得很细数据库设计文档里也标注了每个字段的含义和表之间的关联关系照着源码和文档走一遍你收获的不只是一份源码而是一套完整的项目思维。如果大家在跑通代码的过程中还有什么卡住的地方可以对照文章里整理的排查表逐一过一遍大部分问题都不是技术问题而是配置细节没对齐。本文还有配套的精品资源点击获取