简介这是一套完整的课堂签到微信小程序实战项目面向计算机、数学、电子信息等专业的本科生适用于课程设计、期末大作业及毕业设计选题聚焦教学场景中的多模态签到需求。资源包含前端小程序源码与后端SSMSpringSpringMVCMyBatis框架实现的Java服务支持限时签到、密码验证、手势解锁及GPS位置校验四大核心功能兼顾实用性与技术深度。压缩包共367个文件涵盖40个核心Java业务类、74个编译后Class文件、27个JS逻辑脚本、22个WXML页面结构、23个WXSS样式文件、28个XML配置与映射文件以及MySQL建表SQL、Jar依赖库和项目说明文档等整体18.3MB结构清晰、模块解耦明确。已有31人下载学习可直接导入IDE运行调试配套代码具备典型三层架构特征含多类签到实体Lsign、Psign、Gsign等及其动态条件查询示例为理解微信生态对接、SSM整合开发与地理围栏签到逻辑提供扎实参考。 课堂签到这小东西看着不起眼真做起来坑不少。学生代签、老师点名拖堂、课后统计费劲这些问题在高校里天天上演。我前后花了几周时间做了这套基于微信小程序的课堂签到系统后台用SSM框架加MySQL前端就是微信小程序支持限时签到、密码签到、手势签到和位置签到四种方式。整个项目源码加文档一起整理出来了结构完整拿来就能跑特别适合做课程设计、毕业设计或者想接触真实项目全流程的开发者参考。这套系统解决的核心问题很明确课堂场景下老师能快速发起签到学生能几秒钟内完成打卡后台自动出统计。限时签到适合临时点名密码签到适合配合课堂口令手势签到有点趣味性位置签到则是防代签的王牌。如果你是刚学完Java Web基础、想看看一个完整项目怎么把前后端串起来的人这篇拆解能帮你省掉大量查资料的弯路。1. 整体架构与设计思路为什么必须是SSM 小程序 MySQL这个组合1.1 技术选型背后的真实考量先说结论这套组合放在2025年依旧能打不是因为技术多新而是因为它稳、熟、好招人。很多学校课程设计、实验室项目、创业初期的MVP都会优先叠这套组合。微信小程序作为前端理由非常直接学生不需要安装App打开微信扫一扫或者搜一下就能用还天然跨平台iOS和安卓通吃。对于课堂签到这种高频低强度的场景小程序的轻量体验是最合适的。如果强行做一个原生App光是让学生扫码下载安装这一步就能劝退一半人。后端用SSM也就是Spring SpringMVC MyBatis这是Java Web里最经典的组合。有人可能会问现在都Spring Boot了为什么还要用SSM我的看法是SSM恰恰能让人把底层的原理吃透Spring 的IOC和AOP、SpringMVC的请求分发流程、MyBatis的手写SQL和结果映射每一层都是显式的、可感知的。很多新手直接上Spring Boot框架自动配置了一大堆东西反而不知道请求进去之后到底发生了什么。而且高校课程设计和面试中SSM仍然是高频考点这套代码拿出来就是现成的学习资料。数据库选MySQL没太多好争论的开源免费、生态成熟、占内存小一台普通云服务器就能跑得飞快。签到系统本身的数据量不大核心表就四到六张MySQL完全能轻松扛住。选型的时候有一个很重要的考量点就是整个项目要能在普通配置的机器上跑起来。我见过有人用微服务架构做签到系统十几个服务依赖光启动环境就能折腾一整天这脱离了课堂签到这个场景的本来需求。1.2 核心业务链路与功能模块整个系统按角色划分主要分教师端和学生端两条操作路径。教师端做的是发起和管理签到的动作登录后台、创建课程、发起一次签到、选择签到类型限时/密码/手势/位置、设置参数比如限时的时长、密码的值、签到的有效范围、查看本次签到的实时进度和最终统计结果。学生端做的是参与签到的动作在小程序里登录绑定学号、进入自己的课程列表、查看当前待签到的任务、根据签到类型的提示完成操作点击确认、输入密码、画手势、授权定位、查看自己历史签到记录。完整业务链路是教师创建签到任务 - 服务端生成一条签到的活动记录状态为进行中 - 学生端轮询或下拉刷新获取待签到列表 - 学生提交签到信息 - 服务端校验类型对应的规则 - 校验通过写入签到记录 - 教师端实时展示已签到人数和名单。这里有个容易被忽略但很关键的设计前端展示要快后端校验必须严。学生在小程序里看到的倒计时、位置判断结果、手势绘制反馈这些都可以放在前端做目的是给用户即时反馈但最终这签到算不算数必须由后端根据数据库里的数据重新校验一遍。前端只是体验层后端才是权威层这个思路在后文会在四种签到方式里反复体现。1.3 四种签到方式的适用场景对比签到方式适用场景优点需要关注的坑限时签到正常上课点名、临时突击签到操作最简单学生一键完成时间卡太死会误伤迟到的同学密码签到配合课堂口令、防止学生提前进教室签到灵活、可控密码容易在群里被扩散手势签到课堂氛围活跃一些的场合趣味性强不容易被截图代签绘制体验需要调优校验逻辑要处理到位位置签到固定教室、机房、实验室等有明确地理位置的场所防代签效果最好室内定位精度不稳容易误判这四种方式不是互相替代的关系而是可以组合使用。我在系统里也做了支持比如老师发起一次签到时可以选择密码 位置双重校验或者限时 手势组合防代签效果会好很多。后面第3节会逐个拆解实现细节。2. 数据库与后端签到功能的核心逻辑拆解2.1 MySQL 核心表设计签到系统的表结构不复杂重点是看清楚几个核心表之间的关联关系。我直接按实际项目里的建表语句来讲核心表一共四张。第一张是用户表t_user同时存教师和学生通过role字段区分。CREATE TABLE t_user ( id int(11) NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL COMMENT 登录账号/学号/工号, password varchar(255) NOT NULL COMMENT 登录密码, real_name varchar(50) NOT NULL COMMENT 真实姓名, role tinyint(1) NOT NULL DEFAULT 0 COMMENT 角色0学生1教师, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;第二张是课程表t_course记录课程的基本信息。CREATE TABLE t_course ( id int(11) NOT NULL AUTO_INCREMENT, course_name varchar(100) NOT NULL, teacher_id int(11) NOT NULL COMMENT 任课教师id, location varchar(100) DEFAULT NULL COMMENT 默认上课地点可空, latitude double DEFAULT NULL COMMENT 默认上课地点纬度, longitude double DEFAULT NULL COMMENT 默认上课地点经度, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;第三张是签到任务表t_sign_task这是整个系统的核心表。每发起一次签到就往这里插一条记录签到类型、时间限制、密码、位置坐标都挂在上面。CREATE TABLE t_sign_task ( id int(11) NOT NULL AUTO_INCREMENT, course_id int(11) NOT NULL COMMENT 所属课程, teacher_id int(11) NOT NULL COMMENT 发起签到的教师, type tinyint(1) NOT NULL COMMENT 签到类型1限时2密码3手势4位置, status tinyint(1) NOT NULL DEFAULT 0 COMMENT 任务状态0进行中1已结束, time_limit int(11) DEFAULT NULL COMMENT 限时签到有效时长分钟, password varchar(255) DEFAULT NULL COMMENT 密码签到签到密码加密存储, gesture_pattern varchar(100) DEFAULT NULL COMMENT 手势签到手势路径序列如1-5-9-3-7, latitude double DEFAULT NULL COMMENT 位置签到目标纬度, longitude double DEFAULT NULL COMMENT 位置签到目标经度, distance double DEFAULT NULL COMMENT 位置签到允许误差半径米, start_time datetime NOT NULL COMMENT 签到开始时间, end_time datetime DEFAULT NULL COMMENT 签到截止时间, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;第四张是签到记录表t_sign_record每个学生提交一次签到就写一条记录。这张表是统计出勤的底表。CREATE TABLE t_sign_record ( id int(11) NOT NULL AUTO_INCREMENT, task_id int(11) NOT NULL COMMENT 签到任务id, user_id int(11) NOT NULL COMMENT 签到学生id, sign_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 签到时间, status tinyint(1) NOT NULL DEFAULT 1 COMMENT 签到结果1成功0失败, sign_latitude double DEFAULT NULL COMMENT 学生签到时的经度, sign_longitude double DEFAULT NULL COMMENT 学生签到时的纬度, remark varchar(255) DEFAULT NULL COMMENT 备注信息如失败原因, PRIMARY KEY (id), UNIQUE KEY uk_task_user (task_id, user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;注意t_sign_record里我加了一个联合唯一索引uk_task_user。这是防止一个学生对同一个签到任务重复提交的兜底方案就算前端被绕过了数据库层面也能拦住脏数据。这个细节在文档里也专门标了非常重要。2.2 SSM 后端核心接口设计后端接口设计遵循 RESTful 风格按功能模块划分常用的接口如下接口路径请求方式功能说明关键参数/api/user/loginPOST用户登录username, password/api/course/listGET获取课程列表teacherId 或 studentId/api/course/addPOST新增课程courseName, teacherId/api/sign/createPOST教师发起签到courseId, type, timeLimit, password, gesturePattern, latitude, longitude, distance/api/sign/taskGET获取某个课程当前进行中的签到任务courseId 或 studentId/api/sign/doSignPOST学生提交签到taskId, userId, password, gesturePattern, latitude, longitude/api/sign/recordsGET获取签到记录列表taskId/api/sign/endPOST教师手动结束签到taskId接口的设计逻辑比较直观教师端主要是创建和查询学生端主要是查询和提交。doSign接口是核心中的核心四种签到方式的校验逻辑全部集中在这个方法里用策略模式或者switch分支处理不同类型。下面列一下这个方法的骨架。public Result doSign(SignRequest request) { // 1. 查询签到任务 SignTask task signTaskMapper.findById(request.getTaskId()); if (task null) { return Result.error(签到任务不存在); } // 2. 检查任务状态 if (task.getStatus() 1) { return Result.error(签到已结束); } // 3. 防止重复签到数据库也有一层联合唯一索引兜底 SignRecord exist signRecordMapper.findByTaskIdAndUserId(request.getTaskId(), request.getUserId()); if (exist ! null) { return Result.error(您已经签到过了); } // 4. 根据签到类型分派校验 boolean checkResult false; switch (task.getType()) { case 1: checkResult checkTimeLimit(task); break; case 2: checkResult checkPassword(task, request.getPassword()); break; case 3: checkResult checkGesture(task, request.getGesturePattern()); break; case 4: checkResult checkLocation(task, request.getLatitude(), request.getLongitude()); break; default: break; } if (!checkResult) { return Result.error(签到校验未通过); } // 5. 插入签到记录 SignRecord record new SignRecord(); record.setTaskId(task.getId()); record.setUserId(request.getUserId()); record.setStatus(1); signRecordMapper.insert(record); return Result.success(签到成功); }这段代码有几个值得注意的细节。第3步的重复签到检查实际项目里很多人会漏掉或者只在前端做了判断。我在这里先做了查询又在数据库加了唯一索引双保险。第4步的分派如果后续要扩展新的签到方式只需要加一个case分支不会影响其他代码扩展成本很低。2.3 服务端校验逻辑的完整流程服务端校验是防止签到作弊的关键防线。我逐个说明四种校验的具体实现。限时签到的校验最简单的就是拿服务器当前时间和任务截止时间做比对。boolean checkTimeLimit(SignTask task) { Date now new Date(); if (task.getEndTime() null) { return false; } return now.before(task.getEndTime()); }这里有一个容易踩的坑时间比对一定要用服务器的当前时间不能直接信任客户端传过来的时间戳。我之前在测试时遇到过这样的情况学生把手机本地时间调慢几分钟限时签到就永远在有效期内。因为前端倒计时只是展示用的后端以服务器时间为准这个思路必须贯穿始终。密码签到的校验核心是比对密码。我的做法是在服务端将密码用 BCrypt 加密后存储校验时用 BCrypt 的 matches 方法比对这样做能避免数据库泄露后密码被直接读出来。boolean checkPassword(SignTask task, String inputPassword) { if (task.getPassword() null || StringUtils.isEmpty(inputPassword)) { return false; } // 数据库中存的是BCrypt加密后的密码 return BCrypt.checkpw(inputPassword.trim(), task.getPassword()); }密码签到还有一个容易被忽略的问题密码的失效机制。如果老师创建了密码签到且没有设置截止时间那这个密码理论上在整个进行中状态都有效。所以我在创建密码签到的时候强制要求必须同时设置一个有效时长到了时间自动把任务状态置为已结束密码自然失效。手势签到的校验本质上是字符串序列比对。老师创建签到的时候在教师端画一个手势图案系统把路径转成数字序列比如1-3-5-7-9存到gesture_pattern字段。学生端画完手势后前端把路径转成同样的数字序列提交到后端后端做精确比对。位置签到的校验是所有方式里最复杂的涉及经纬度距离计算。我用的数学公式是 Haversine 公式可以比较精准地计算地球球面上两点之间的距离。公式看起来长但实现并不复杂。boolean checkLocation(SignTask task, double latitude, double longitude) { if (task.getLatitude() null || task.getLongitude() null || task.getDistance() null) { return false; } double distance calculateDistance(latitude, longitude, task.getLatitude(), task.getLongitude()); return distance task.getDistance(); } public double calculateDistance(double lat1, double lon1, double lat2, double lon2) { double radLat1 Math.toRadians(lat1); double radLat2 Math.toRadians(lat2); double a radLat1 - radLat2; double b Math.toRadians(lon1) - Math.toRadians(lon2); 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) )); // 地球半径取6371千米 return s * 6371000; }这里有个小技巧Haversine 公式的计算结果单位是米函数返回时直接乘地球半径 6371000就把单位转成了米。我在第一次写的时候忘了换算结果判断半径100米变成了100千米所有签到全部通过一查数据才发现小数点差了一千倍。后来我干脆写了个单元测试传入两个相差1公里的坐标点断言结果在900到1100米之间防止回归错误。3. 四种签到方式的实现原理与代码级细节3.1 限时签到前端倒计时与后端时间兜底限时签到的核心体验是一个倒计时。前端拿到签到任务的end_time之后用setInterval每秒刷新一次显示剩余时间。这里必须注意前端倒计时显示的是相对于服务器结束时间的差值而不是前端本地算出来的时间。小程序端的实现思路是打开签到页面时先通过接口拿到签到任务的start_time和end_time前端用这两者的差值做倒计时。同时要处理一个边界情况如果打开页面时任务已经过期了直接展示签到已结束。在实际开发中有一个体验优化的细节不要等到倒计时归零才让服务器去更新任务状态。因为前端展示归零了但服务器状态可能还是进行中学生仍然可能提交成功。反过来也一样如果服务器已经判定结束但前端还在倒计时学生提交会得到失败提示体验很割裂。我的处理方案是学生端在提交签到之前先调用一个轻量接口查询任务状态如果任务已经结束就直接阻止提交尽量避免失败的请求。限时签到的时间粒度也值得注意。我在后台设置里允许老师精确到分钟最短1分钟最长30分钟。建议正常上课用5到10分钟比较合适太短了学生来不及掏出手机太长了就失去了限时的意义。3.2 密码签到密码生成、防扩散与防重放密码签到的逻辑本身不复杂但有几个设计决策会影响实际使用效果。第一个决策是密码由谁生成。我采用了两种方式老师自己输入自定义密码或者系统随机生成一个6位数字密码。自定义密码的好处是老师可以把密码和课堂内容关联起来比如今天是20250314系统随机则更防猜测。两种方式我都保留了后端判断一下传参是否为空即可。第二个决策是密码怎么传递给学生。这是很多项目容易忽略的地方。如果系统直接把密码随签到任务下发给学生端那密码签到就形同虚设因为学生完全可以提前在群里收到同步推送的密码然后迟到的时候再签到。正确的做法是密码不随任务列表下发老师通过课堂口头公布、或者在班级群/黑板等线下渠道告知学生。学生拿到密码后在小程序里输入前端把密码传到后端后端校验通过才算签到成功。这样能最大限度保证人必须在课堂。第三个决策是防重放。所谓重放就是一个学生签到了把密码分享给另一个还没到教室的人那个人也能输入同样的密码签到成功。对于密码签到这个场景很难完全防住重放因为密码是共享的。但可以增加辅助手段限制密码签到的有效时间比如老师设置5分钟有效过了时间任务关闭或者叠加位置校验密码和位置同时通过才能签到成功。我在系统里做了组合校验功能一个签到任务可以同时挂多种校验规则这是最有效的防作弊手段。3.3 手势签到九宫格图案的生成、绘制与校验手势签到的实现比前两种更有趣前端工作量也更大一些。九宫格手势的基本模型是3乘3的九个点从上到下、从左到右分别编号1到9。老师设置手势时前端用canvas绘制九宫格监听触摸事件记录手指滑过的点编号比如先滑过1再滑过5最后滑到9路径序列就是1-5-9。系统要求至少连接4个点防止设置过于简单的手势。小程序端的核心代码片段大致如下// pages/gesture/gesture.js 关键部分 const points [ {x: 50, y: 50}, {x: 150, y: 50}, {x: 250, y: 50}, {x: 50, y: 150}, {x: 150, y: 150}, {x: 250, y: 150}, {x: 50, y: 250}, {x: 150, y: 250}, {x: 250, y: 250} ]; Page({ data: { selected: [] }, onTouchStart(e) { // 根据触摸点坐标判断落入哪个九宫格区域 const pos this.getTouchPosition(e); if (pos ! null) { this.addPoint(pos); } }, getTouchPosition(e) { const x e.touches[0].x; const y e.touches[0].y; for (let i 0; i points.length; i) { if (Math.abs(x - points[i].x) 40 Math.abs(y - points[i].y) 40) { return i 1; } } return null; }, addPoint(num) { let selected this.data.selected; // 避免重复点 if (!selected.includes(num)) { selected.push(num); this.setData({selected: selected}); this.drawGesture(); } }, onTouchEnd() { const pattern this.data.selected.join(-); // 提交pattern到后端 wx.request({ url: this.data.baseUrl /api/sign/doSign, method: POST, data: { taskId: this.data.taskId, userId: this.data.userId, gesturePattern: pattern }, success: (res) { // 处理结果 } }); } });这里的核心点是前端不传绘制轨迹的坐标点数组而是转成数字序列传给后端。为什么因为直接传坐标点数组会带来两个问题一是坐标点数组很大网络传输浪费流量二是坐标点受屏幕分辨率、手指按压位置影响同一手势在不同设备上画出来的坐标点不完全一致后端很难做模糊匹配。而转成数字序列后只要用户画的轨迹经过相同的关键点序列就是一致的校验简单又可靠。手势校验的一些经验如果手势绘制老失败要先确认是否是触摸区域判定过宽或过窄导致的关键点误判。我调参的时候把每个点的命中半径设为40像素以屏幕宽度375为例九宫格间距100像素40像素的命中范围手感比较适中。还有一个细节九宫格是网格状排列手指画完之后路径可能经过相邻点导致多出一些不该有的中间点。我的处理方式是只有在候选点与原点的连线距离足够近时才加入序列并且落点在两个点边界时取最近的那个点这样能有效减少误选。3.4 位置签到经纬度距离计算与误差处理位置签到是小程序端最依赖系统能力的一种方式用的是微信小程序的wx.getLocation接口。wx.getLocation({ type: gcj02, isHighAccuracy: true, success: (res) { // res.latitude 纬度, res.longitude 经度, resid.accuracy 误差范围米 wx.request({ url: this.data.baseUrl /api/sign/doSign, method: POST, data: { taskId: this.data.taskId, userId: this.data.userId, latitude: res.latitude, longitude: res.longitude } }); }, fail: (err) { // 用户拒绝授权或定位失败 } });有几个细节必须注意。type要传gcj02这是中国国测局坐标系微信定位返回的经纬度默认就是gcj02。如果你在后端直接拿这个经纬度和地图上点的坐标计算距离而地图坐标用的是wgs84或者bd09就会有几十到几百米的偏差很容易导致误判。定位精度是另一个重点。微信返回的accuracy字段代表误差范围单位是米。在室内环境下GPS信号弱定位误差很容易达到几十米甚至上百米。所以位置签到的有效半径不能设太死。我的建议是室内教室场景半径设20到50米比较合理室外操场或者大范围场地可以设到100到200米。如果设得太小比如5米那学生站在教室门口就可能签到失败体验很差。室内定位还有一个常见难题同一栋教学楼里不同教室的经纬度可能非常接近学生站在隔壁教室也能签到这个教室的到。针对这个问题我在系统里增加了楼栋 教室的辅助校验选项。老师在后台创建签到时可以选择经纬度范围校验和教室编码校验的组合方式学生端除了上报定位还需要在页面上确认自己所在教室的编码两个都对才算签到成功。这个方案不是百分百防作弊但能有效减少误判和代签。4. 从源码到跑通完整部署与联调记录4.1 需要准备的工具和环境拿到源码之后先在本地把运行环境搭起来。我按自己实测过的环境来列这个组合经过了多轮验证兼容性最好。JDK 1.8 及以上推荐1.8和大部分服务器环境一致Maven 3.6 及以上用来打war包和管理依赖Tomcat 8.5 或 9.0MySQL 5.7 及以上推荐5.78.x也能跑需要注意驱动版本微信开发者工具最新稳定版即可IDEA 或者 Eclipse后端开发工具IDEA推荐一台可以联网的电脑开发调试用启动顺序建议是先装MySQL导入数据再启动Tomcat跑后端最后用微信开发者工具打开小程序目录。如果顺序反了比如先打开小程序后端没启动页面会一直报请求失败容易误判是代码问题。4.2 数据库初始化与后台启动的完整步骤第一步是创建数据库。这里有一个细节我建议建库的时候显式指定 utf8mb4 字符集因为签到记录里的备注信息可能包含emoji表情utf8mb4才能完整支持。CREATE DATABASE IF NOT EXISTS sign_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE sign_system; source /路径/sql/sign_system.sql;第二步是修改数据库连接配置。源码里的jdbc.properties文件是核心配置把数据库地址、用户名、密码改成你自己的。jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/sign_system?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password你的数据库密码注意serverTimezone这个参数。MySQL 8.x 默认时区是UTC如果不显式设置serverTimezoneAsia/Shanghai会导致时间和本地区时间差8个小时签到记录的sign_time和时间判断会全乱。第三步是打包部署。在项目根目录执行mvn clean package -DskipTests等构建完成后在target目录下会生成sign_system.war文件。把这个war包拷贝到Tomcat的webapps目录下启动Tomcatwar包会自动解压部署。第四步是验证后台是否正常。浏览器访问http://localhost:8080/sign_system/api/user/login如果返回一个 JSON 格式的报错比如{code:500,msg:参数不能为空}说明后台已经成功跑起来了。如果直接访问报404检查一下war包文件名和访问路径是否一致通常Tomcat会以war包名作为上下文路径。4.3 小程序端配置与联调小程序端的联调配置核心就是请求的 baseURL。在源码的utils/config.js文件里把baseUrl改成你本机的局域网IP。module.exports { // 本地联调用局域网IP不要用localhost真机访问不到 baseUrl: http://192.168.1.100:8080/sign_system, // 上线后换成一个已备案且支持HTTPS的域名 // baseUrl: https://yourdomain.com/sign_system }为什么本地联调要用局域网IP而不是localhost因为如果你用手机真机预览小程序手机访问localhost会指向手机自己根本访问不到你电脑上的后端服务。正确做法是手机和电脑连同一个WiFi电脑上查一下局域网IP填进去。然后用微信开发者工具导入小程序项目在详情 - 本地设置里勾选不校验合法域名、web-view业务域名、TLS版本以及HTTPS证书。这是开发阶段必须勾的选项否则小程序会拦截所有 HTTP 请求。注意这个选项只对开发调试有效上线之后要在小程序后台配置合法域名并强制HTTPS。联调时建议按这个顺序自测先用模拟器登录教师账号创建一个限时签到任务再用模拟器登录学生账号完成一次签到最后去数据库t_sign_record表里看这条记录有没有落库。如果这三步都通了基础链路就没问题再逐个测试其他三种签到方式。4.4 上线前必须处理的三件事如果只是本地跑着玩上一节的内容已经够了。但如果要真正部署到服务器上有三件事必须提前处理。第一件事是域名和HTTPS。微信小程序规定线上环境所有请求必须是 HTTPS 协议且域名必须在小程序后台配置到服务器域名的 request 合法域名列表里。你需要准备一个已备案的域名给后端配好HTTPS证书再把baseUrl改成https://你的域名/sign_system。这个流程里最容易卡住的是SSL证书配置大多数云厂商都提供免费证书申请Nginx 或者 Tomcat 的配置照着官方文档来做就能搞定。第二件事是定位权限声明。小程序里用到wx.getLocation在提审的时候需要在小程序后台填写用户隐私保护指引明确说明收集位置信息的目的和用途同时要把所需接口权限 - getLocation对应的用途说明写清楚。不填这个审核会被驳回。第三件事是服务器时区和数据库时区统一。最简单粗暴的方式是云服务器设置成北京时间Asia/ShanghaiMySQL 连接串里加上serverTimezoneAsia/ShanghaiTomcat 启动参数里加上-Duser.timezoneAsia/Shanghai。保证三层时区一致签到时间才不会出现诡异的8小时偏移。5. 踩坑实录签到系统最常见的7个问题这个项目从开发到调试到上线我踩了不少坑也帮好几个朋友排查过问题。把最常见的问题整理成速查表遇到类似情况可以直接对号入座。问题现象产生原因排查思路解决方案小程序请求接口报url not in domain list合法域名没配置或没勾选开发选项检查开发者工具是否勾选了不校验合法域名开发阶段勾选该选项上线前配置合法域名后台启动就报数据库连接失败数据库连接配置错误或MySQL没启动检查jdbc.properties的账号密码和IP确认MySQL已启动账号密码正确驱动版本匹配限时签到倒计时和实际时间差很多时区不一致导致时间偏移查看数据库时间和服务器时间是否一致统一设置为Asia/Shanghai检查serverTimezone参数位置签到室内定位漂移到隔壁楼GPS室内信号弱误差加大查看前端返回的accuracy值增大校验半径或叠加教室编码辅助校验手势签到画对了但校验失败序列生成可能多选点或漏选点打印学生的提交序列和老师的预设序列对比调整触摸命中半径优化候选点取最近逻辑密码签到密码输对了还报失败密码前后有空格或大小写不一致检查前端传参是否做trim后端比对时统一trim和去除空白字符部署到云服务器后请求超时安全组/防火墙没放行8080端口服务器上telnet一下端口是否通在云控制台安全组规则里放行对应端口下面挑几个重点问题展开讲一下排查过程。限时签到倒计时神奇的多出8小时有一次测试老师在16:00发起签到限时5分钟学生端却显示还有5小时5分钟。排查下来发现是数据库时间没问题但Tomcat所在的云服务器时区是UTC导致new Date()获取到的时间比北京时间慢了8个小时倒计时自然就多出8小时。后来我把 Tomcat 启动参数加了-Duser.timezoneAsia/Shanghai同时在数据源配置里也显式指定了serverTimezoneAsia/Shanghai问题就解决了。手势签到序列始终对不上本地测试时老师设置的手势是1-5-9-3-7学生端画同样的路径提交上去的序列却变成了1-2-5-9-3-7。打日志一看才知道手指在1号点滑向5号点的时候路径经过了2号点的区域被误判定为选中了2号点。我的处理办法是在addPoint方法里增加一个范围判断逻辑只有当前触摸位置离候选点足够近并且在上一个点和当前点的连线方向基本一致时才判定为经过该点。同时把9个点的命中半径从40像素调到了35像素减少边界误触。位置签到距离计算偏差巨大在办公室里测试手机显示定位精度10米但实际计算出来的距离和真实距离差了100多米。后来发现是坐标系的问题。我之前直接拿地图API获取的教室坐标WGS84坐标系和微信返回的坐标GCJ02坐标系做计算两个坐标系之间的偏移量在几十到几百米不等。最终方案是老师的坐标也用微信小程序获取统一走gcj02坐标系两边都在同一套坐标体系里距离计算就准确了。这个经验查了很多资料才确认建议写文档的时候重点标注。密码签到老有人输错被锁最初密码校验失败就提示密码错误然后让学生重新输入。但有些学生连续输错三五次被记了很多条失败记录后台统计看起来不干净。后来我在失败记录上做了标记不过不限制尝试次数而是把失败原因写清楚比如密码错误、签到任务不存在、签到已结束这样老师和学生都能快速定位问题。部署到云服务器后请求超时这个问题经常出现在第一次部署的同学身上。本地跑得好好的部署到云服务器后小程序请求全部超时。排查思路是先看服务器本地的Tomcat日志确认后端是否正常启动然后在本地用浏览器访问http://服务器IP:8080/sign_system/api/user/login如果访问不了看云控制台的安全组和服务器防火墙是否放行了8080端口。安全组规则配置不对是最大的嫌疑我在腾讯云和阿里云上都踩过检查一下入站规则里有没有放行TCP 8080端口。再多说一个高频问题MySQL 8.x 下跑项目报驱动类找不到。原因是SSM框架的 pom.xml 里配置的 MySQL 驱动版本可能过老把mysql-connector-java的版本升到8.0.x以上同时注意驱动类名要改成com.mysql.cj.jdbc.Driver老驱动类名在新驱动里已经不推荐了。这个坑很隐蔽很多人本地用5.7没问题一上服务器换成8.0就完蛋实际上就是驱动版本和驱动类名不匹配导致的。整套系统做到这个程度功能上已经比较完整了。实际使用中我做的最多的扩展是接入了课程表导入和签到数据导出Excel。老师端上传课程表Excel后台解析后批量建课每节课结束后一键导出到课名单减轻教学秘书的统计压力。如果你拿这套代码去做课程设计或者毕设这两个方向都是很好的加分点。签到页面的用户体验也值得持续打磨。比如学生端在签到成功后做一个轻快的动画反馈让签到动作有完成感或者在限时签到即将结束时页面背景色变红进行提示。这些优化代码量不大但对实际使用体验的提升非常明显。最后再分享一个实际操作中的小技巧联调阶段准备两个微信开发者工具窗口一个登录教师账号一个登录学生账号左右分屏同步操作。这样调试创建签到和学生签到的全流程时不用来回退出登录效率能提升一大截。我最初就是用两个窗口并行调试省下了大量反复登录的时间。本文还有配套的精品资源点击获取