
简介一份完整的基于Android平台开发的考勤系统项目资源包覆盖客户端源码、服务端源码及配套数据库面向Java/Android开发者适合作为毕业设计或课程设计的综合实践参考。资源包为RAR压缩格式大小约19.92MB共包含1016个文件其中Java源文件、class文件、JSP页面、XML配置与布局、JAR依赖库、APK安装包、SQL脚本及图片资源等一应俱全既有Android客户端的交互界面与网络通信实现也有服务端的接口逻辑和数据库表结构。已有775人浏览学习适合需要系统掌握Android网络编程、服务端RESTful API设计以及数据库建模的读者。该案例详细展示了登录认证、GPS定位打卡、请假申请、考勤记录查看等核心模块并讨论了Spring Boot、JWT、Retrofit、SQLite等关键技术在真实业务场景中的综合运用同时提供推送通知、报表统计、数据备份等扩展思路能够帮助开发者从整体上理解移动考勤系统的架构设计与开发流程。1. 拿到“Android考勤系统”源码包先别急着解压一个名为“基于Android考勤系统(客户端源码服务端源码数据库).rar”的压缩包在很多刚接触企业级移动应用的开发者手里往往意味着三样东西一段可演示的毕业设计、一份能跑通的课程项目、或者一个需要改造成生产系统的业务雏形。这个标题本身已经把系统边界写得很清楚——它不是单机Demo而是一个完整的前后端分离工程Android端负责员工打卡、请假和考勤查询服务端处理登录鉴权、打卡记录写入和统计计算数据库存放员工、部门、请假和每日考勤流水。和市面上大量只有客户端的仿写项目不同这类源码包的价值在于它同时暴露了客户端、接口层和数据层的三方契约换句话说你能从一份代码里看到“打卡”这一个动作从手机到数据库的完整旅程。但拿到压缩包后发现工程跑不起来是这类项目最常见的第一道坎。不是代码缺了某个类而是三端之间的版本、依赖和配置彼此不匹配。我会从工程解压开始逐步搭建可运行环境拆解客户端打卡主流程梳理服务端接口和数据库表设计最后给出联调排错和二次开发的关键参数。如果你正在拿它做毕业设计、课程设计或者想在此基础上改造成企业微信/钉钉风格的考勤应用这篇内容可以帮你省掉大量试错时间。2. 客户端源码解析从Android打卡主流程看懂定位与状态机2.1 客户端工程结构MVC分层与模块划分解压客户端源码后第一件事不是用Android Studio打开等Gradle同步而是先在文件管理器里看一遍目录结构。常见做法是分为activity、fragment、adapter、model、utils、network、service几个包名。如果代码组织得好你会看到LoginActivity、MainActivity、CheckInActivity、AttendanceRecordActivity这类类名它们的职责边界通常对应了登录模块、主页框架、打卡页面和历史记录页面。考勤系统客户端的核心页面按重要程度排序依次是打卡页、考勤记录页、请假申请页和个人中心页。工程结构能直接看出这个系统的成熟度。如果model包里只有几个Java Bean而缺少Repository层说明网络请求逻辑直接写在Activity里改造时需要抽离如果network包里有Retrofit的ApiService接口和统一的ResponseData包装类说明结构做得还算规范二次开发成本低。Android客户端的核心链路基本是用户在打卡页点击按钮客户端获取定位信息连同用户Token和当前时间一起提交到服务端服务端返回打卡结果并展示。2.2 考勤打卡的时间与距离计算LocationManager与System.currentTimeMillis2.2.1 定位权限与Provider选择Android考勤系统最核心的代码逻辑集中在“获取当前定位并计算与公司坐标的距离”这几十行。老项目通常用LocationManager获取GPS或网络定位新项目会换用FusedLocationProviderClient。两者的切换不是简单的API替换还涉及权限声明、定位开关检测和Android 6.0以上运行时权限适配。下面这段代码展示的是兼容性较好的实现方式private void getLocation() { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) ! PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_LOCATION); return; } LocationManager lm (LocationManager) getSystemService(LOCATION_SERVICE); Location location lm.getLastKnownLocation(LocationManager.GPS_PROVIDER); if (location null) { location lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); } if (location ! null) { double distance calculateDistance( location.getLatitude(), location.getLongitude(), COMPANY_LATITUDE, COMPANY_LONGITUDE); if (distance ALLOWED_DISTANCE) { submitAttendance(location.getTime()); } } }这段代码体现了考勤系统的两个关键设计一是定位来源的回退机制——GPS不可用或信号弱时自动换用网络定位二是距离判定阈值ALLOWED_DISTANCE通常设为100到300米之间。getLastKnownLocation拿的是最后一次缓存位置存在时间偏移风险更严谨的做法是使用requestSingleUpdate向Provider请求一次实时定位不过那会引入回调逻辑和超时处理很多课程设计项目会省略这一步。2.2.2 打卡状态机正常、迟到、早退与缺卡客户端提交打卡后服务端返回的不只是“成功”或“失败”而是一个考勤状态码状态码是前后端之间的重要契约。状态码设计通常用一个数字字段表示0代表正常1代表迟到2代表早退3代表缺卡4代表请假。判断逻辑放在服务端而非客户端的原因有两点一是客户端系统时间可以被用户手动修改伪造打卡时间二是考勤班次规则可能随时调整改服务端比强制升级App更轻量。客户端拿到状态码后在UI层展示不同颜色和文案private String parseAttendanceStatus(int status) { switch (status) { case 0: return 正常; case 1: return 迟到; case 2: return 早退; case 3: return 缺卡; case 4: return 请假; default: return 未知; } }客户端里这类辅助方法通常放在utils包中它隐藏的一个信息是客户端不承担考勤结果的计算只做展示。这让客户端源码的量看起来比想象中少也意味着如果你只想做一个纯客户端的考勤Demo就必须把迟到早退判断逻辑自己写在本地但那种做法到了生产环境会被时间篡改问题直接击穿。2.3 离线补卡与本地缓存SQLite与SharedPreferences的取舍2.3.1 用SQLite记录未提交打卡考勤App最常见的异常场景是打卡时网络不通或者服务器超时。直接提示“网络错误请重试”会让员工错过打卡时间因此部分源码会加入“离线打卡”设计本地生成一条打卡记录状态标记为“待同步”等网络恢复后批量提交。这需要客户端有一个轻量级数据库SQLite是必然选择因为它不需要引入额外的ORM框架SQLiteOpenHelper就能完成建表和增删改查。下面是一个简单的本地打卡记录表CREATE TABLE local_checkin ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, punch_time TEXT NOT NULL, latitude REAL, longitude REAL, sync_status INTEGER DEFAULT 0 );sync_status字段是这张表的核心设计0代表待同步1代表已同步。每次打卡先插入这张表并标记为待同步同时立即展示“打卡成功待同步”给用户后台静默重试提交服务端返回成功后把状态改为1。如果服务端一直不可达sync_status始终为0下一次登录时可以增加一个“待同步记录”列表供用户手动处理。这个机制能大幅提升考勤系统的可用性也会在代码评审中被看作是加分项。3. 服务端源码与数据库设计考勤接口的契约与落库3.1 服务端工程选型与架构分层服务端源码的常见技术栈是Spring Boot结合MyBatis或Spring Data JPA配合MySQL存储业务数据。拿到服务端源码后先看pom.xml或build.gradle确认依赖版本再看application.yml里的数据库连接配置。分层方式通常是Controller接收请求、Service处理业务逻辑、Mapper或Repository操作数据库。考勤系统的接口数量不需要很多核心就是登录、打卡、查询记录、请假审批这几个但接口的返回格式必须统一。最常见的返回包装类是public class ResponseDataT { private int code; // 200成功400参数错误401未登录500服务器错误 private String message; private T data; }code字段在考勤系统里承担非常重要的职责。比如打卡接口可能返回200表示成功、201表示已在打卡时间段内重复打卡、202表示不在允许的打卡范围内、203表示迟到但已记录。客户端拿到不同的code会展示不同的Toast或Dialog而不是简单判断HTTP请求是否成功。3.2 打卡接口从坐标校验到防重复提交3.2.1 考勤接口的鉴权与Token传递服务端使用Token机制保持会话状态常见实现是登录成功后用UUID生成一个Token存储到Redis或数据库表中客户端在HTTP请求头里携带。Spring Boot侧用拦截器统一校验Component public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (token null || !tokenService.isValid(token)) { response.setStatus(401); return false; } return true; } }AuthInterceptor拦截所有/api/**请求只有通过校验的请求才能到达Controller。考勤系统的打卡、查询、请假接口都必须走这个拦截器因为打卡记录和员工工号是强绑定的服务端不能信任客户端传来的用户ID必须从Token中解析出当前用户。这是很多课程设计源码容易犯的错误——把用户ID放在请求参数里让客户端随便传导致任何人的打卡记录都能被伪造或篡改。3.3 考勤数据库的核心表结构从部门到打卡流水数据库脚本通常是attendance.sql文件用Navicat或命令行直接导入。一个完整的考勤系统数据库至少有四张表部门表department、员工表employee、打卡记录表attendance_record、请假表leave_request。其中打卡记录表是所有查询统计的核心典型建表语句如下CREATE TABLE attendance_record ( id BIGINT AUTO_INCREMENT PRIMARY KEY, employee_id BIGINT NOT NULL COMMENT 员工ID, work_date DATE NOT NULL COMMENT 工作日期, check_in_time DATETIME COMMENT 上班打卡时间, check_out_time DATETIME COMMENT 下班打卡时间, status TINYINT DEFAULT 0 COMMENT 0正常 1迟到 2早退 3缺卡 4请假, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_employee_work_date (employee_id, work_date) ) COMMENT每日考勤记录表;uk_employee_work_date这个唯一键是数据库层面防止同一天重复打卡的兜底方案。即使服务端漏掉了重复提交校验数据库也会因为唯一索引而拒绝第二条记录。check_in_time和check_out_time拆成两个字段而不是只存一条punch_time是因为考勤统计经常需要计算“实际工作时长”拆开存放才能用SQL直接计算。如果只有一条打卡记录计算工时就需要对多行做聚合逻辑会复杂得多。3.4 迟到早退的统计SQL与GROUP BY聚合查询数据库层最有价值的代码是考勤统计的聚合查询。最常见的场景是“查询某部门一个月内每个人的迟到次数”SELECT e.employee_name, COUNT(CASE WHEN a.status 1 THEN 1 END) AS late_count, COUNT(CASE WHEN a.status 2 THEN 1 END) AS early_leave_count, COUNT(CASE WHEN a.status 3 THEN 1 END) AS absent_count FROM employee e LEFT JOIN attendance_record a ON e.id a.employee_id LEFT JOIN department d ON e.department_id d.id WHERE d.department_name 技术部 AND a.work_date BETWEEN 2025-11-01 AND 2025-11-30 GROUP BY e.id, e.employee_name;这段SQL用了LEFT JOIN而不是INNER JOIN目的是把没有打卡记录的员工也查出来——比如新入职员工没有任何考勤记录COUNT会返回0而不是直接缺失这一行。GROUP BY后面必须把e.name也加进去否则在严格模式的MySQL 5.7中会因为ONLY_FULL_GROUP_BY报错这在Navicat里执行时尤其常见。4. 联调、构建与排错Android Studio到服务端的常见故障4.1 Android Studio构建必须处理的三个参数用Android Studio打开客户端源码后最优先检查的是build.gradle里的compileSdkVersion、minSdkVersion和targetSdkVersion这三个参数决定了微信扫码式调试的底线。targetSdkVersion如果高于29需要在AndroidManifest.xml里为定位添加ACCESS_FINE_LOCATION和ACCESS_BACKGROUND_LOCATION权限minSdkVersion如果低于21代码里就不能直接用java.time包需要改用SimpleDateFormat处理时间。定位权限在AndroidManifest.xml声明与运行时授权是两套体系很多源码只写了uses-permission没有在代码里做requestPermissions回调处理导致Android 9以上设备点击打卡直接闪退。修复方法是检查所有使用LocationManager的类是否在onRequestPermissionsResult里重新触发了定位逻辑。4.2 服务端本地跑通与模拟器调试的配置要点服务端源码导入IDE后先修改application.yml中的数据库账号密码再执行SQL脚本初始化表结构。Spring Boot项目通常用8080端口但Android模拟器访问宿主机要用10.0.2.2真机则需要用局域网IP。这行配置是联调成败的关键# Android 模拟器访问本机服务端 adb reverse tcp:8080 tcp:8080 # 或直接使用模拟器专用地址 http://10.0.2.2:8080/api/adb reverse命令将模拟器的8080端口映射到开发机的8080端口客户端代码里写http://10.0.2.2:8080即可。真机调试则需要保证手机和电脑在同一局域网把BaseUrl改成电脑的局域网IP。同时要确认AndroidManifest.xml里允许了明文HTTP通信——android:usesCleartextTraffictrue否则Android 9默认拦截所有HTTP请求服务端明明在跑却返回“网络连接失败”。4.3 数据库连接、时间一致性与定位漂移的排查考勤系统联调中有一个极其隐蔽的坑服务端数据库时间和客户端时间不一致。打卡保存用的是System.currentTimeMillis()还是NOW()在服务端决定但“是否迟到”的判断如果放在客户端就会因为手机时间不准而出现误判。一个简单的排查规范是生产环境中考勤规则相关的计算一律以服务端时间为准客户端只负责提交行为触发时刻的Unix时间戳。另一个高频故障是定位漂移。在室内场景下GPS信号弱NETWORK_PROVIDER返回的坐标可能偏离真实位置几百米导致员工明明在公司楼下却提示不在打卡范围。常见的缓解做法是拿最近三次定位坐标取平均值再用均值与公司坐标做距离计算同时对超过100米的异常跳点做丢弃处理。如果服务端源码里有一个DistanceUtil工具类优先查看它的距离计算算法是球面距离还是直线距离两者在几百米范围内差值不大但到几公里外会显著不同。5. 二次开发进阶补卡审批、统计报表与班次规则拿到这套考勤系统源码后大部分人的诉求是“加功能”而不是“重写”。最常被要求新增的两个功能一是补卡审批流程二是统计报表导出。补卡功能本质上是对attendance_record表的status字段做状态流转员工提交补卡申请后记录标记为4请假或新增一个5补卡待审主管审批通过后改为0正常。这个逻辑在数据库插入一条leave_request类型的记录同时把打卡记录的status置为待审状态比在客户端做弹窗要可靠得多。统计报表导出则建议直接用SQL聚合后返回给前端生成Excel而不是逐条拉取打卡记录在Android端计算。一个月的打卡记录可能有几万行全部下发到手机既耗流量又卡界面更好的方式是在服务端写好统计接口Android端只展示汇总后的ListAttendanceStatisticDTO。班次规则调整是比较隐蔽但实用的扩展点。源码里常用的做法是在department表中增加work_start_time和work_end_time两个字段再把判断“迟到”的逻辑从写死的9:00改为读取部门表配置。如果某天公司临时实行弹性打卡比如10点前算正常只需改数据库记录而不用重新发版App这对运维来说是一个非常实用的收益。值得注意的是考勤源码中时间处理容易出现“UTC还是本地时间”的交叉问题建议服务端统一用LocalDateTime存储客户端展示时再转成指定时区格式不要在数据库层混用DATE_FORMAT和NOW()否则统计出来的数据很难定位问题。本文还有配套的精品资源点击获取