ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Android考勤系统开发实战:定位、打卡与服务端交互全解析

Android考勤系统开发实战:定位、打卡与服务端交互全解析 简介基于安卓的考勤系统是一份完整的毕业设计或课程设计参考项目面向安卓应用与服务端开发的学习者解决企业考勤管理中打卡、请假、权限控制等实际需求。压缩包共1016个文件大小19.92MB文件类型以Java源码、class编译文件、XML配置、JSP页面、JAR依赖库、PNG/GIF图片和SQL脚本为主其中Java源码145个、class编译文件208个并包含可直接安装运行的APK以及服务端工程与数据库初始化文件目录结构清晰。资源已有775人学习下载。内容不仅包含客户端登录、GPS定位打卡、请假申请与考勤记录模块还配套服务端接口和数据库表设计数据库脚本中覆盖用户、考勤、请假、角色权限等核心表结构可帮助理解Retrofit网络通信、Spring Boot后端开发、SQLite/MySQL存储、JWT安全验证等关键知识点。从源码结构到数据库设计均有完整呈现便于二次开发与功能扩展适合正在准备毕业设计或想系统提升移动端与后端综合开发能力的学习者对照演练。1. 这个考勤系统到底在解决什么问题很多团队的考勤还停留在微信群里打卡或者指纹机上前者没法校验位置后者没法覆盖外勤人员。标题里这套「基于Android考勤系统」就是典型的学生项目或企业内部轻量方案的形态一个 Android 客户端负责采集打卡动作、GPS 定位和展示记录一个服务端负责接收请求、计算距离并统一写库数据库单独成文件方便重置和迁移。拆开这个.rar你拿到的是三个能独立运行又互相依赖的部分所以第一步不是急着打开 Android Studio而是把三者的边界画清楚。我通常建议先把数据库建好因为服务端的接口和客户端的字段都围着表结构转。服务端源码决定业务规则放在哪数据库存储用户、部门、考勤流水和请假申请客户端只做两件事拿到当前经纬度上报然后把服务端返回的「打卡成功/迟到/外勤」结果渲染出来。对 5 年以上的人来说这套系统的价值不在架构而在它把移动端、HTTP 服务、关系型数据库三块串成了完整的闭环课程设计、毕业设计或者给内部小团队改造成考勤工具都够用。2. Android 考勤系统客户端目录结构、服务器地址与打卡主流程2.1 先读懂客户端源码的包结构和主入口解开客户端源码后第一件事是看AndroidManifest.xml里注册了哪些权限。考勤客户端离不开三种权限ACCESS_FINE_LOCATION、INTERNET和ACCESS_NETWORK_STATE。缺少第一个定位回调不会触发缺少第二个所有 HTTP 请求都会在客户端直接抛UnknownHostException。常见项目里包名一般长这样com.company.attendance下面会有activity、adapter、utils、model四个包。activity里通常有LoginActivity、MainActivity、HistoryActivity三个核心页面。model包里是User、AttendanceRecord这些 POJO字段名必须和服务端返回的 JSON key 完全一致。如果你打开某个实体类发现字段比接口返回的少那不是错误是开发者只挑了客户端用得到的字段。// 客户端标准打卡请求实体字段要和JSON保持一致 public class PunchRequest { private String userId; private double latitude; // 纬度 private double longitude; // 经度 private String punchTime; // 打卡时间格式 yyyy-MM-dd HH:mm:ss }注意看punchTime这个字段很多初学者让 Android 客户端生成时间传上去这是错的。客户端系统时间可以被用户手动改服务端必须用自己的LocalDateTime.now()重新生成打卡时间客户端传的时间只能作为参考字段保留。这个细节直接决定考勤数据有没有法律效力。2.2 改服务器地址的三处位置少改一处就白调拿到源码后你大概率需要把服务端跑在本地电脑上这就涉及 IP 修改。Android 客户端里服务器地址通常放在三处utils/Constants.java或utils/HttpUtils.java里的常量、res/values/strings.xml里的server_url、以及build.gradle里的buildConfigField。// Constants.java 或 HttpUtils.java 里的典型配置 public class Constants { // 10.0.2.2 是 Android 模拟器访问宿主机的固定地址 // 真机调试时改成电脑的局域网IP例如 192.168.1.100 public static final String BASE_URL http://10.0.2.2:8080; public static final String PUNCH_URL BASE_URL /api/punch; public static final String LOGIN_URL BASE_URL /api/login; public static final String HISTORY_URL BASE_URL /api/history; }这里有个新手必踩的坑如果用的是 Android Studio 自带模拟器localhost指的是模拟器自己不是你的电脑。必须用10.0.2.2。如果用的是真机就得用ipconfigWindows或ifconfigMac查到电脑在局域网里的 IP并且保证手机和电脑连的是同一个 WiFi。光改这一处还不够服务端所在电脑的防火墙要放行 8080 端口否则客户端请求会一直卡在连接超时而服务端控制台一个日志都没有。如果你在 Android Studio 里跑客户端还要确认AndroidManifest.xml里开了android:usesCleartextTraffictrue否则 Android 9 以上默认禁止明文 HTTP 请求。2.3 定位、去重与手势判定打卡页面通常就是一个大按钮点击后调用LocationManager拿经纬度然后走 OkHttp 或 HttpURLConnection 提交。为了减少依赖很多课程设计用的还是HttpURLConnection。不管哪种方式有两个细节值得注意定位要设置setTime和setDistance避免拿到长时间缓存的位置打卡需要加防重复点击的逻辑。// 经典打卡逻辑先读定位再提交服务端 btnPunch.setOnClickListener(v - { if (isPunching) return; // 防止重复提交 isPunching true; Location location getLocation(); if (location null) { Toast.makeText(this, 定位失败请检查GPS, Toast.LENGTH_SHORT).show(); isPunching false; return; } PunchRequest req new PunchRequest( userId, location.getLatitude(), location.getLongitude(), new SimpleDateFormat(yyyy-MM-dd HH:mm:ss).format(new Date()) ); sendPunchRequest(req); });定位获取是这套系统最不稳定的环节。getLocation()里如果只拿了GPS_PROVIDER室内基本永远拿不到值我一般会同时监听NETWORK_PROVIDER并且做一次「谁先到用谁」的判断。打卡之后客户端要清理定位监听器不然会持续耗电这在课程设计答辩时经常被老师追问。3. 服务端源码从接口设计到时区处理3.1 直接看接口层别被分层套路带偏这套标题里的服务端源码常见实现是 SSMSpring SpringMVC MyBatis或 Spring Boot。如果你拿到的是 SSM 结构注意Controller层只做参数接收业务逻辑在Service层数据库操作在Mapper层。建议优先读AttendanceController和UserController这两个文件能告诉你系统对外提供了哪些能力。一般超不出这四个接口登录、打卡、查历史记录、请假申请。RestController RequestMapping(/api) public class AttendanceController { Autowired private AttendanceService attendanceService; // 打卡接口接收客户端经纬度计算与公司坐标的距离 PostMapping(/punch) public Result punch(RequestBody PunchRequest request) { // 业务规则距离小于500米视为在岗打卡否则标记为外勤 double distance GeoUtils.distance( request.getLatitude(), request.getLongitude(), companyLatitude, companyLongitude ); if (distance 500) { return attendanceService.doPunch(request.getUserId(), 内勤); } return attendanceService.doPunch(request.getUserId(), 外勤); } }注意这段代码里GeoUtils.distance是关键多数项目不会真的用高德或百度地图 SDK因为那要申请 Key而是自己实现一个球面距离公式。服务端拿到经纬度后算距离判定是内勤还是外勤再落库。判定的阈值一般就写在application.properties或Constants里别写死在代码中。改这个阈值不需要重新编译对答辩演示很有帮助。3.2 时区和跨天问题80% 的考勤 bug 都出在这服务端代码里最容易出问题的不是并发是时间。国内服务器统一用东八区还好说但如果你在本地 Windows 上调试而数据库又是默认的 UTC就会出现打卡时间比实际时间慢 8 小时的现象。排这种问题先看三处application.properties里的连接串是否带了serverTimezoneAsia/ShanghaiJVM 启动参数是否设了-Duser.timezoneGMT8以及数据库连接会话是否执行了SET time_zone 8:00。这三处各管一层漏掉任何一个都可能复现「早上 9 点打卡库里显示凌晨 1 点」的灵异事件。还要注意迟到判定不能直接用当前时间和 9:00:00 比较要先把日期截断再比时间否则会把昨天的迟到记录带出来。3.3 服务端源码里的三个必调参数参数位置建议值原因server.portapplication.properties8080客户端 BASE_URL 里写死的端口必须一致spring.datasource.urlapplication.propertiesjdbc:mysql://localhost:3306/attendance?serverTimezoneAsia/Shanghai缺了时区参数会差 8 小时mybatis.mapper-locationsapplication.propertiesclasspath:mapper/*.xml如果改了 Mapper 文件位置不配这里启动直接报找不到语句我见过不少人卡在服务端起不来十有八九是 MySQL 没启动或者密码不对。这里给个建议拿到源码后先把application.properties里的数据库账号密码和本地对齐再用 Postman 直接调接口客户端先放一边。这样能把问题快速隔离到某一层而不是客户端、服务端、数据库三边同时猜。4. 数据库考勤系统的表设计、初始化数据与连接故障排查4.1 三张核心表的设计与建表语句打开数据库源码里的.sql文件考勤系统最少有三张表t_user用户表、t_attendance考勤记录表、t_leave请假表。复杂的还会有t_department部门表和t_attendance_config考勤规则表。建表语句值得逐行读一遍特别是字段注释注释里写的是「上班表」还是「状态标识」决定了你对整个业务逻辑的理解。-- 考勤记录表注意唯一索引这个细节 CREATE TABLE t_attendance ( id INT NOT NULL AUTO_INCREMENT, user_id INT NOT NULL COMMENT 用户ID, punch_time DATETIME NOT NULL COMMENT 打卡时间, type TINYINT DEFAULT 0 COMMENT 0-上班 1-下班, status VARCHAR(20) DEFAULT 正常 COMMENT 正常/迟到/早退/外勤, latitude DECIMAL(10,6) DEFAULT NULL, longitude DECIMAL(10,6) DEFAULT NULL, PRIMARY KEY (id), KEY idx_user_time (user_id, punch_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;idx_user_time这个联合索引是查询历史记录的关键HistoryActivity里按用户和时间范围刷列表就是靠它。没有这个索引数据量过万后页面会明显变慢报表统计也会拖垮数据库。所以拿到源码后不要只建表要把索引也一并建上。4.2 初始化数据从哪来.rar里的数据库文件通常有两种形态一种是.sql纯脚本用 Navicat 或命令行直接执行另一种是.db文件SQLite。如果是 MySQL 脚本打开后先看有没有INSERT INTO t_user的语句。有的项目图省事只建表不插数据你要自己造测试账号密码。注意很多课程设计里的密码是 MD5 加密后存进去的不能直接明文改库。你在 Navicat 里手动把密码改成123456后发现登录一直失败就是因为服务端比对的是 MD5 后的值。-- 如果脚本里没有初始数据可以用这条插入测试账号 -- 假设服务端用 MD5 加密123456 对应的密文固定 INSERT INTO t_user (username, password, real_name, department_id) VALUES (admin, e10adc3949ba59abbe56e057f20f883e, 管理员, 1);如果脚本附带了data.sql务必先打开看编码格式。UTF-8 的脚本被以 GBK 打开再执行中文名会全部变成乱码而且这种乱码进了表很难清洗只能删表重建。我一般用命令行执行而不是图形化工具在 MySQL 命令行里执行source /path/to/attendance.sql报错信息更清晰能直接定位到第几行语法有问题。4.3 连接不上的常见原因MySQL 8 还是 5.7 的问题考勤系统服务端连接 MySQL 时最常见的报错是Public Key Retrieval is not allowed。这是因为 MySQL 8 默认使用 caching_sha2_password 认证插件。处理方式是在 JDBC 连接串后面加allowPublicKeyRetrievaltrueuseSSLfalse。如果是 5.7 版本则不需要这两个参数。不确认版本前不要乱改先把SELECT VERSION();查出来再决定。另外如果你把.sql文件导入的是 SQLite 而不是 MySQL需要先检查服务端源码里DriverManager注册的是com.mysql.cj.jdbc.Driver还是org.sqlite.JDBC这决定了整个数据层方言SQLite 和 MySQL 的建表语句兼容性很差基本不能直接套用。Navicat 连接不上达梦或 MySQL 时先排除数据库服务本身没有启动——Windows 上在「服务」里看 MySQL 是否运行Linux 用systemctl status mysql。连接失败十有八九是端口被防火墙拦了而不是密码错了。5. 用 adb 验证客户端上报的数据是否真的写入了 MySQL5.1 通过 adb shell 抓 LocationManager 的真实收入服务端接口通了数据库也能连了最后一步是验证客户端到底有没有把坐标传对。你不需要看 Android Studio 的 Logcat用 adb 就能完成。先让手机用 USB 连上电脑开启开发者模式然后执行下面的命令抓取系统中所有 LocationManager 的客户端的坐标值。# 查看所有已连接的设备 adb devices # 抓取最近一次定位结果验证经纬度是否合理 adb shell dumpsys location | grep last location -A 5如果返回的lat和lng都是 0.0说明客户端还没拿到定位问题不在网络而在定位权限或 GPS 没开。如果经纬度数值看起来正常比如纬度 30 多经度 120 多就把这两个值复制下来用 Postman 直接手动 POST 到服务端的/api/punch接口。这样能把网络层完全绕开纯粹验证服务端和数据库这条链路。真机调试时我习惯先用adb shell settings put secure location_mode 3强制打开定位服务因为很多手机默认关闭定位导致getLastKnownLocation永远返回 null。5.2 定位去重与考勤边界这步做完才算真正可交付验证通过之后再看一个实战细节附近的地铁站、公交站会产生大量重复定位请求导致同一分钟内出现多条打卡记录。处理办法是在服务端加一个「同一用户两分钟内不可重复打卡」的判定。用一条 SQL 就能解决-- 同一用户两分钟内只允许一条记录INSERT 前先 SELECT SELECT COUNT(*) FROM t_attendance WHERE user_id 1 AND punch_time NOW() - INTERVAL 2 MINUTE;如果查询结果大于 0直接返回「打卡过于频繁」根本不用走后续的距离计算。这个判断放在 SQL 里比放在 Java 代码里更稳妥因为多实例部署时本地变量无法跨实例同步。另外一个常被忽略的点是status字段不能只由服务端算客户端最好也显示一次服务端返回的结果让用户立刻看到自己是不是迟到了。把返回的Result对象用Toast弹出来比任何日志都直观。到这一步这套考勤系统的完整链路就全部跑通了剩下的就是你要不要给t_leave表加一条审批状态字段的小事了。本文还有配套的精品资源点击获取
返回列表