ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

基于Ruoyi+uniapp的宿舍考勤系统:三种签到模式实战落地

基于Ruoyi+uniapp的宿舍考勤系统:三种签到模式实战落地 简介基于若依框架和uniapp构建的前后端分离学生宿舍考勤管理系统面向Java全栈开发者适用于课程设计、毕业设计或中小型校园项目二次开发。系统支持口头点名、普通签到、自定义范围位置签到、二维码、人脸识别、九宫格手势和签到码等多种签到方式集成阿里云人脸识别与OSS存储并支持微信小程序密码登录与授权登录配套可视化看板可展示考勤统计和异常记录。压缩包共82个文件以Java源码、class编译文件为主另有yml/properties配置、XML映射、PNG/JPG截图和说明文档便于理解项目结构体积仅1.77MB轻量易部署。已有172人学习下载适合研究若依权限体系、uniapp跨端开发或考勤业务的开发者源码中包含完整签到流程与云端服务调用示例可直接参考迁移。基于Ruoyiuniapp的学生宿舍考勤管理系统实战口头点名、普通签到、位置签到三种模式怎么落地前阵子帮一所院校做宿舍考勤管理系统需求看着特别简单把晚间查寝从纸质登记搬到手机上。可真动起手来才发现考勤这两个字背后牵扯的口头点名签到、普通签到、位置签到自定义范围各自要处理的业务逻辑完全不一样。这个项目最终选了Ruoyi若依前后端分离版本做后端管理端用uniapp写学生和宿管使用的移动端整体跑下来从开发到上线大约两周。这篇文章就把这个基于若依uniapp的学生宿舍考勤管理系统完整落地过程分享出来从架构设计、数据库建模到后端接口实现、uniapp位置签到再到联调踩坑一次性说透给正在做类似前后端分离项目的人一个可以直接参考的路线。1. 宿舍考勤管理的真实痛点三种签到模式是怎么来的宿舍考勤和课堂考勤不是一回事。课堂考勤判断的是人是否在教室宿舍考勤判断的是这个学生今晚到底住没住在宿舍里。后者牵涉到宿管查寝、辅导员统计、退宿纠纷、家长沟通等多个环节如果不落系统全是微信接龙和Excel登记数据零散不说每周汇总一次能把人逼疯。1.1 宿舍考勤和课堂考勤的差异课堂考勤的场景是一个老师对几十个学生教室固定时间固定用点名或者刷卡就能解决。宿舍考勤的场景是宿管对整栋楼几百号学生楼层分散宿舍分散时间窗口又往往集中在晚上九点到十一点之间。更麻烦的是宿舍考勤还分日常住宿考勤和特殊事件考勤例如节假日返校统计、晚间查寝、临时排查每种场景要求不一样。这决定了系统不能只做一个签到按钮。宿管可能需要在走廊里挨个口头确认学生今晚在不在宿舍这是口头点名签到对于日常常规查寝可以让学生主动打开App点一下这是普通签到但如果碰到辅导员想确认学生确实在宿舍范围内就必须用位置签到而且不同宿舍楼、不同校区的范围需要自行设定这就是标题里那个被截断的位置签(自——位置签到自定义范围。1.2 三种签到模式的适用场景和差异口头点名签到宿管拿手机逐层查寝口头核对人数然后在名单上快速标记到、未到、请假。适合时间紧、学生配合度低、需要宿管把关的场景。前端核心是名单操作效率要能连续点击少弹窗。普通签到学生自行打卡适合走读生登记、活动签到等对位置要求不高的场景。核心是幂等防重复先到先记过期不再放行。位置签到自定义范围以宿舍楼为圆心设定一个半径范围学生必须在这个地理范围内才能签上。核心是定位精度和坐标统一。这三种模式不建议拆成三张核心表而是用一张考勤任务表加一张签到记录表用字段区分类别后面统计报表会非常方便。这个设计决策在后面数据建模部分详细说。1.3 为什么选Ruoyiuniapp这套组合选Ruoyi若依的时候我看中的不是它的代码生成器而是权限体系。宿舍考勤天然分角色宿管只能看自己那栋楼辅导员只能看自己带的学生学工处能看全校。若依前后端分离版本自带用户、角色、菜单、数据权限这些都能直接复用我只需要扩展考勤模块省下大量基础框架开发时间。移动端选uniapp的理由更实际学生手里安卓、iOS、微信小程序五花八门用uniapp一套代码可以同时出App和微信小程序宿管不用额外装App小程序里就能完成点名操作。后端Spring Boot移动端uniapp管理端用若依自带的Vue页面正好是一个标准的Spring Boot后端多端前端的前后端分离架构。2. 架构分层与数据建模先别写代码把表和关联理清楚很多新人拿到这个需求第一反应是建表做CRUD但宿舍考勤的关键不在CRUD而在签到状态怎么流转位置范围怎么校验权限怎么隔离。这三件事决定表结构。2.1 前后端分离架构下各端职责整个系统分三层后端Spring Boot基于Ruoyi-Vue版本改造。负责登录鉴权、考勤任务管理、签到记录落库、位置范围校验、统计报表。管理端若依自带的Vue管理后台给学工处和辅导员用创建考勤任务、配置位置签到范围、查看统计结果。移动端uniapp构建打包成App和微信小程序。学生端负责普通签到、位置签到宿管端额外拥有口头点名操作权限。通信统一走RESTful API登录后拿token请求头带Authorization。移动端和管理端共用同一套后端接口只是移动端对应的路由权限更少。这样管理端管理任务移动端执行签到数据双向打通。2.2 核心表结构设计我先列三张核心表这是在项目里实际用到的结构做了脱敏简化。第一张是宿舍与住宿关系表解决学生住哪栋楼哪个宿舍的问题CREATE TABLE dorm_student ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_id BIGINT NOT NULL COMMENT 学生用户ID, dorm_id BIGINT NOT NULL COMMENT 宿舍ID, status TINYINT DEFAULT 1 COMMENT 1在住 0搬离, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_student_dorm (student_id, dorm_id) );第二张是考勤任务表CREATE TABLE attend_task ( id BIGINT PRIMARY KEY AUTO_INCREMENT, task_name VARCHAR(100) NOT NULL COMMENT 任务名称, task_type TINYINT NOT NULL COMMENT 1口头点名 2普通签到 3位置签到, dorm_building_id BIGINT COMMENT 关联宿舍楼, start_time DATETIME NOT NULL, end_time DATETIME NOT NULL, late_time DATETIME COMMENT 迟到判定时间, latitude DECIMAL(10,6) COMMENT 位置签到中心纬度, longitude DECIMAL(10,6) COMMENT 位置签到中心经度, radius INT DEFAULT 200 COMMENT 位置签到范围半径单位米, create_by VARCHAR(64), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, remark VARCHAR(500) );第三张是签到记录表这是整个系统的核心CREATE TABLE attend_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, task_id BIGINT NOT NULL COMMENT 考勤任务ID, student_id BIGINT NOT NULL COMMENT 学生用户ID, sign_time DATETIME NOT NULL COMMENT 签到时间, status TINYINT NOT NULL COMMENT 1正常 2迟到 3未到 4请假 5补签, sign_type TINYINT NOT NULL COMMENT 签到方式1本人签到 2宿管代签 3管理员补录, operator_id BIGINT COMMENT 实际操作人ID本人签到时为空, address VARCHAR(255) COMMENT 定位地址描述, latitude DECIMAL(10,6) COMMENT 签到时的纬度, longitude DECIMAL(10,6) COMMENT 签到时的经度, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_task_student (task_id, student_id, DATE_FORMAT(sign_time, %Y-%m-%d)) );看到最后那个唯一索引了吗这是防重复签到的兜底方案。光靠后端代码判断会漏掉并发场景数据库唯一索引加上后学生连点两次签到第二次插入直接报DuplicateEntry接口里捕获这个异常返回已签到即可。这个设计会在第3章继续展开。2.3 位置签到的范围和坐标方案怎么定位置签到不是把GPS坐标存下来就完事关键在于判断学生是否在范围内这一动作放在哪端做。我强烈建议放在后端做不要放在uniapp端判断。原因是前端判断逻辑容易被绕过而且不同手机型号返回的定位精度不一样前端只负责采集坐标后端统一校验才能保证规则一致。那范围怎么定管理端在若依后台通过地图选点组件选宿舍楼保存中心点经纬度和半径。后端拿到学生的坐标后用球面距离公式算出两点距离再和半径比较。坐标系统必须统一否则会出现学生明明站在宿舍楼下却提示不在范围内的诡异问题。国内常用坐标系有WGS84、GCJ02、BD09三种uniapp的uni.getLocation返回的是GCJ02高德坐标系管理端做地图选点如果也统一用高德就不需要转换这个细节坑了不少人第5章细说。3. 后端改造在若依框架里落地考勤业务若依框架的改造重点不在怎么写接口而在怎么在既有体系里扩展业务模块。直接用代码生成器生成三张表的CRUD然后再往里面填考勤业务逻辑效率最高。3.1 考勤任务发布与管理考勤任务的管理端接口比较常规创建、修改、删除、分页查询。需要注意两点第一任务状态不要用定时任务去更新。很多同学一看到进行中/已结束就想上Quartz定时刷状态其实完全没有必要。查询任务时直接和当前时间比较动态计算状态避免定时任务没跑或者跑挂导致状态不一致。第二位置签到任务创建时前端会传过来中心点经纬度和半径。后端可以做一个简单的校验_radius_限制在50到1000米之间防止管理员误填导致整个校区都能签到。任务发布的伪代码如下if (task.getTaskType() 3) { if (task.getLatitude() null || task.getLongitude() null || task.getRadius() null) { return error(位置签到必须配置中心点和范围); } if (task.getRadius() 50 || task.getRadius() 1000) { return error(签到范围应在50到1000米之间); } }3.2 签到接口的校验逻辑与距离计算移动端调用签到接口时后端按这个顺序校验解析token拿到当前用户ID。查询任务判断当前时间是否在startTime和endTime之间不在则直接拒绝。查attend_record表看今天这个任务是否已经签到过签过直接提示不用重复签。根据taskType进入不同分支普通签到直接落库。位置签到用当前坐标和任务中心点算距离超过radius拒绝。口头点名学生端不调用这个接口是宿管端发起。距离计算直接用我写好的这个工具方法Haversine公式适合几百米范围内的判断private double distance(double lat1, double lng1, double lat2, double lng2) { double radLat1 Math.toRadians(lat1); double radLat2 Math.toRadians(lat2); double a radLat1 - radLat2; double b Math.toRadians(lng1) - Math.toRadians(lng2); double s 2 * Math.asin(Math.sqrt( Math.pow(Math.sin(a / 2), 2) Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2))); return s * 6371000; }这个公式返回的是米。以宿舍楼为中心_radius_设置200米时一般能覆盖宿舍楼加上楼下广场的区域。实测下来GPS在室外正常精度下误差在10到50米之间200米半径足够。3.3 口头点名的批量提交与数据权限控制口头点名是最特殊的一种模式因为发起方不是学生而是宿管。宿管在移动端选择口头点名任务系统拉出该宿舍楼所有在住学生名单宿管挨个确认状态最后批量提交。后端接口设计为PostMapping(/sign/batch) public AjaxResult batchSign(RequestBody BatchSignDTO dto) { // dto: taskId, records: [{studentId, status}] // 遍历records逐条写入attend_recordoperatorId取当前登录人 }这里有个重要的权限细节宿管只能提交自己负责的宿舍楼的学生名单不能越权。若依自带的DataScope注解在纯MyBatis场景下很好用但在这个批量提交场景里需要手动加一层判断——先根据taskId查出任务关联的dormBuildingId再判断当前登录人是不是这栋楼的宿管不是就直接拒绝。后续查询考勤记录时仍然可以用若依的数据权限保证辅导员看不到其他学院的数据。4. uniapp移动端三种签到场景的前端实现要点移动端是学生和宿管直接接触的部分体验不好会被骂到怀疑人生。这一章重点讲uniapp端三个关键实现请求封装、签到交互、定位处理。4.1 请求封装与登录态处理uniapp开发时最忌讳在每个页面里直接写uni.request最好统一封装一个request方法。我的做法是const request (url, method GET, data {}) { return new Promise((resolve, reject) { uni.request({ url: BASE_URL url, method, data, header: { Authorization: Bearer uni.getStorageSync(token) }, success: (res) { // 若依统一返回code 200表示成功 if (res.data.code 200) { resolve(res.data); } else if (res.data.code 401) { uni.navigateTo({ url: /pages/login/login }); } else { uni.showToast({ title: res.data.msg, icon: none }); reject(res.data); } }, fail: (err) reject(err) }); }); };若依的后端登录接口返回token移动端登录时保存token到storage每次请求带在Header里。注意若依管理端默认有验证码App端不建议展示图形验证码否则体验很割裂。我当时扩展了一个appLogin接口走用户名密码登录不校验验证码接口内部仍然走若依的登录逻辑。4.2 普通签到和口头点名的交互实现普通签到的前端逻辑很直白进页面先调查询接口拿到今天的任务和签到状态如果已签到按钮置灰显示签到时间。没签到的话点击后调签到接口成功后更新本地状态。口头点名的前端要复杂一些因为宿管要在一屏里快速确认几十个学生的状态。我实现的方案是列表项滑动切换状态默认未处理点击状态标签循环切换正常/未到/请假已处理项背景色稍微变化右下角显示已确认数量。底部一个大按钮提交点名结果提交前检查是否有遗漏未处理的名单。这个交互实测比弹窗选择效率高一倍宿管反馈很好用。这是一个很值得做的体验细节。4.3 位置签到的定位处理与高频坑位置签到核心代码不复杂uni.getLocation({ type: gcj02, isHighAccuracy: true, highAccuracyExpireTime: 4000, success: (res) { this.latitude res.latitude; this.longitude res.longitude; this.submitSign(); }, fail: (err) { uni.showToast({ title: 定位失败请检查GPS权限, icon: none }); } });但坑全在周边配置上manifest.json里必须配置定位权限描述否则部分手机直接拒绝定位请求。App端需要在pages.json对应页面配置requiredPrivateInfos定位权限说明否则上架应用市场会被拒。微信小程序端用户拒绝定位后再次点击不会重新弹授权必须引导用户去设置页手动打开需要调用uni.openSetting。签到按钮点击后要加loading状态防止用户连续点击触发多次定位请求。还有一个需要注意的点如果学生在室内、走廊里GPS信号弱定位精度可能掉到上百米这时候200米半径也可能判定失败。我当时的处理方案是定位失败或精度太低时允许用户手动输入一个备注说明然后提交待人工审核管理员在后台看位置日志判断。这个兜底方案很实用上线后避免了很多纠纷。5. 部署联调中的踩坑记录与建议项目开发和部署过程中免不了会遇到一些环境级别的问题。每个问题单看都能解决但堆在一起就会炸心态我把印象最深的几个记录下来。5.1 若依改造中最容易忽略的地方若依框架功能多但做移动端联调时有两个点要特别注意第一个是验证码。若依默认开启验证码App登录走不了验证码流程最开始我卡了一下午。解决办法是在SysLoginService里扩展一个无验证码登录方法或者在配置中心加一个开关App登录接口单独放行。第二个是跨域。前后端分离项目如果前端是H5部署跨域必须处理若依自带CorsConfig已经解决但如果用了nginx反向代理要注意代理配置里忽略跨域头。另外Redis缓存这块不要忽略。若依用Redis缓存验证码和在线用户信息部署时如果Redis端口或密码配置不对后端启动不报错但登录接口会一直失败。检查ruoyi的application.yml里的Redis配置是排在第一位的。5.2 位置签到误差、重复提交、缓存一致性问题位置签到上线后最大的问题不是功能而是坐标系不一致。管理端选点用的高德地图后端存储的经纬度是GCJ02移动端定位返回的也是GCJ02全链路一致才没出问题。如果你管理端用了百度地图选点那存进去的是BD09坐标和移动端GCJ02坐标直接计算距离偏差可能达到几百米学生明明站在楼下却签不了到。这是位置签到项目里最常见的坑一定先确认全链路的坐标系。重复提交的处理也踩过坑。一开始只靠接口层if判断结果测试同事快速连点两次两条记录就进去了。后来加了数据库唯一索引才彻底解决。所以我在第2章说唯一索引是兜底这个设计一定要留。还有一个容易忽略的细节若依自带的数据权限缓存。学生用户登录后如果辅导员在后台把他的学生状态改了移动端可能还拿着旧缓存的宿舍信息。这时候让学生重新登录一次或者后台主动清理用户缓存就能避免。5.3 一点个人经验用若依加uniapp做这种前后端分离的管理系统最大的收益是省下了基础框架的搭建时间最大的成本在于理解若依本身的权限和数据隔离机制。如果你打算自己动手做类似系统我的建议是按这个顺序推进先跑通若依代码生成把三张表的CRUD生成出来再实现登录和移动端请求打通然后实现普通签到接着做位置签到最后做口头点名和统计报表。每一步都验证通过再进下一步不要想着一次性全做出来再联调那种方式出了问题根本没法定位。再分享一个小技巧考勤统计报表是辅导员最看重的东西但恰恰是这个功能最容易在开发后期被压缩。我在设计签到记录表时就预留了sign_time、status、sign_type三个字段的组合查询索引后面做按宿舍楼按日期汇总出勤率只需要一条分组查询完全不用再造表。这种提前量设计推荐每个人都养成习惯。本文还有配套的精品资源点击获取
返回列表