尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

课堂签到微信小程序全解析:SSM+MySQL实现四种防作弊签到模式

课堂签到微信小程序全解析:SSM+MySQL实现四种防作弊签到模式 简介这是一套完整的课堂签到微信小程序实战项目面向计算机、数学、电子信息等专业的本科生适用于课程设计、期末大作业及毕业设计选题解决传统课堂人工点名效率低、代签难监管等问题。项目采用前后端分离架构前端为微信小程序支持限时签到、密码验证、手势解锁与GPS位置校验四重签到模式后端基于SSMSpringSpringMVCMyBatis框架开发数据库使用MySQL配套完整SQL脚本与配置文件。压缩包共367个文件含40个核心Java业务类、74个编译后Class文件、28个XML配置与映射文件、27个JS逻辑脚本、23个WXSS样式及22个WXML页面结构文件另有SQL建表语句、项目说明文档与依赖JAR包整体18.3MB。已有31人下载学习资源结构规范、模块职责清晰包含多类型签到的Controller层调度、Service业务封装及Mapper数据操作可直接运行并作为SSM整合实践与小程序对接的优质参考范例。 校园里最头疼的事情之一就是点名。大课上百号人一个个喊“到”能浪费十分钟代签、截屏转发更是一抓一个准。所以当我决定做一个课堂签到微信小程序时核心诉求就三个快、准、防作弊。再加上后端用SSM框架和MySQL支撑限时、密码、手势、位置四种签到模式基本覆盖了高校课堂、培训机构、小型会议的全部场景。这篇文章就完整拆解这个项目的源码结构、数据库设计、每种签到的实现逻辑以及我在实际部署和调试中踩过的坑。适合正在做毕业设计、课程设计或者想快速搭建一套轻量考勤系统的开发者参考。1. 项目整体设计与技术选型思路1.1 为什么是微信小程序 SSM MySQL 这个组合先聊技术选型。微信小程序做前端是理所当然的选择学生不用装额外App扫一扫或者搜一下就能进教师端也能在同一个小程序里切换身份省去了维护两套客户端的成本。微信官方提供的wx.getLocation、wx.startLocationUpdate等API也为位置签到提供了原生支持相比H5页面在权限获取和定位精度上更省心。后端选择SSMSpring Spring MVC MyBatis而不是Spring Boot很多人会觉得奇怪。但说实话高校的课程设计、毕业设计甚至不少小公司的内部项目SSM依然有大量存量代码。Spring负责Bean管理和事务控制Spring MVC处理前后端路由MyBatis作为ORM映射数据库这个组合的分层非常清晰——Controller层管接口、Service层写业务、Mapper层做数据持久化。对于签到这类业务逻辑并不复杂、但需要清晰演示“三层架构”的项目SSM反而比Spring Boot更适合做教学演示和代码答辩。MySQL作为数据库也没有悬念开源免费、安装方便、JDBC驱动成熟5.7和8.0版本都兼容这个项目。唯一要注意的是如果使用MySQL 8.0驱动名要从com.mysql.jdbc.Driver换成com.mysql.cj.jdbc.Driver连接URL也建议加上serverTimezoneAsia/Shanghai和useSSLfalse否则会出现时区报错和SSL握手警告。1.2 四种签到模式的设计意图为什么要做四种签到方式而不是只要一个“点击签到”按钮是因为不同场景下的防作弊需求完全不同。限时签到应对的是“课后补签”问题——老师在下课前临时发起一个5分钟的签到窗口过时就关闭学生无法事后补点。密码签到应对的是“二维码转发代签”——签到时需要输入老师口头公布或PPT上显示的口令杜绝了截图转发。手势签到比较灵活适合小班课堂或内部会议老师画一个简单图形作为当日口令趣味性强也能防拍照。位置签到应对的是“人不在教室但远程打卡”——通过GPS或Wi-Fi定位校验限制学生必须在课堂所在建筑的一定范围内才能签到成功。这四种模式不是互相替代而是互补关系。实际使用中完全可以用组合策略比如“限时 位置”双重校验先判断学生在不在范围内再判断是否在时间窗口内无论从产品设计还是从技术实现角度都比单一口令要严密得多。2. 数据库设计与核心表结构说明2.1 六张业务表的职责划分这个项目的数据库设计遵循了典型的“用户-班级-签到-记录”四层模型。完整建表SQL在项目文档里有我挑核心的几张表说一下设计思路。用户表t_user是学生和老师共用的通过role字段区分身份0表示学生1表示老师这样避免了建两张表造成的冗余。关键字段包括openid、nickname、avatar、student_no学号、class_id所属班级。openid必须加唯一索引因为微信支付和登录都需要靠它来识别用户身份重复绑定会引发逻辑混乱。课程表t_course主要字段是course_name、teacher_id、start_time、end_time、classroom、latitude、longitude。注意这里存了经纬度是给位置签到用的。课堂表t_class可以理解为班级实体字段为class_name、grade、major把班级和专业年级绑定方便教师按班级维度发签到任务。签到任务表t_sign_task是核心中的核心。每次老师发起签到都会在这里生成一条任务记录CREATE TABLE t_sign_task ( id int(11) NOT NULL AUTO_INCREMENT, course_id int(11) DEFAULT NULL COMMENT 课程ID, teacher_id int(11) DEFAULT NULL COMMENT 发起签到的老师ID, sign_type tinyint(4) DEFAULT NULL COMMENT 签到类型1限时 2密码 3手势 4位置, start_time datetime DEFAULT NULL COMMENT 签到开始时间, end_time datetime DEFAULT NULL COMMENT 签到截止时间, pwd varchar(255) DEFAULT NULL COMMENT 密码或手势数据, status tinyint(4) DEFAULT 1 COMMENT 1进行中 0已结束, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;最后一个关键是签到记录表t_sign_record学生每次成功签到的明细都写在这里。字段包括task_id、user_id、sign_time、location、is_valid。is_valid字段很重要用于标记这条签到是否有效方便老师后台做“作废异常签到记录”操作。这张表的数据量会随着使用逐渐变大建议在task_id和user_id上建联合索引查询“某次签到哪些人未到”或“某人历史出勤率”时效率高很多。2.2 为什么使用utf8mb4而不是utf8这是一个很多人容易忽略的细节。MySQL的utf8字符集是阉割版只支持最多3字节的字符而微信用户的昵称里经常会出现emoji表情4字节如果表结构是utf8插入昵称带表情的用户时会直接报错Incorrect string value: \xF0\x9F\x98\x80。所以全部表统一使用utf8mb4编码排序规则用utf8mb4_general_ci或utf8mb4_unicode_ci都可以。这里建议在创建数据库时直接指定默认字符集CREATE DATABASE IF NOT EXISTS sign_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;另外连接字符串里也要加上characterEncodingutf8否则即使数据库是utf8mb4JDBC连接层依然可能因为编码不一致而乱码。这是我在调试阶段真正踩过的坑查了一下午才定位到是连接参数的问题。2.3 数据表之间的关系拆解整体关系不复杂老师创建课程课程绑定班级老师对某门课程发起签到任务学生在某个课程下签到形成记录。用SQL查“某次签到哪些人已到”其实非常简单SELECT u.real_name, u.student_no, r.sign_time FROM t_sign_record r LEFT JOIN t_user u ON r.user_id u.id WHERE r.task_id #{taskId} AND r.is_valid 1;反过来查询谁没到就是先查该课程班级的全部学生再排除掉上面查到的已到名单。这个操作在MySQL里用NOT IN或LEFT JOIN IS NULL都能实现数据量大时建议用后者性能更稳定。项目的SSM架构里这个查询放在Mapper的XML文件中通过association和collection完成对象关联映射逻辑清晰也好维护。3. 四种签到模式的技术实现细节拆解3.1 限时签到的倒计时与状态控制限时签到是四种模式里逻辑最简单、但状态控制最容易出错的一种。核心点在于不要让前端来控制时间窗口的开关必须以后端时间为准。前端展示的倒计时来自后端返回的end_time与当前时间戳的差值但每次发起签到请求时后端都要重新校验当前服务器时间是否在start_time和end_time之间。为什么因为小程序端的本地时间可以被用户篡改把手机时间改回签到开始之前前端时间校验就形同虚设。所有安全判断必须落在服务器端。后端判断逻辑用Java的LocalDateTime实现public Result signByTime(Integer taskId, String openid) { SignTask task signTaskMapper.selectById(taskId); LocalDateTime now LocalDateTime.now(); if (now.isBefore(task.getStartTime()) || now.isAfter(task.getEndTime())) { return Result.error(不在签到时间范围内); } // 继续后续的幂等判断、记录插入 }时间区间校验完成后还有一层幂等判断同一个openid对同一个task_id不能重复签到否则刷新一下页面就能刷出N条记录。这个幂等判断在数据库层面也同样要兜底给t_sign_record的task_id和user_id加唯一索引双保险。实际使用中我还推荐做一个“提前XX秒结束签到”的容错机制。因为前端倒计时到0的时候网络请求还可能在路上如果正好卡在临界点用户明明在有效期内点击了签到后端却因为一秒之差拒绝体验会很差。解决方案是后端在判断过期时留5秒的缓冲if (now.isAfter(task.getEndTime().plusSeconds(5))) { return Result.error(签到已结束); }3.2 密码签到的加密存储与防暴力破解密码签到看起来简单就是老师设置一个口令学生输入后校验。这里最容易犯的错误是明文存储。签到密码虽然生命周期只有几分钟到几小时但依然不建议明文存放在数据库里因为一旦数据库泄露历史签到密码可以被用来批量伪造签到记录。项目中的做法是前端只负责把用户输入的密码传到后端后端把密码拼上一个固定盐值后用MD5加密再和数据库中的密文比对String inputPwd DigestUtils.md5Hex(rawPwd SALT); SignTask task signTaskMapper.selectById(taskId); if (!task.getPwd().equals(inputPwd)) { return Result.error(密码错误请重试); }密码错误不能无限重试否则学生可以写个脚本循环遍历4位数字密码。在Service层加一个简单的失败计数器同一个task_id openid连续错误5次就锁定3分钟。这个逻辑不需要额外建表用Redis加过期key是最优雅的但SSM项目如果没接Redis用ConcurrentHashMap 过期时间也能实现单机版的限制。密码签到还有一个易忽略的问题密码本身的字符集限制。如果老师设置的是中文口令URL传参时会出现编码问题。建议前端在提交前用encodeURIComponent编码后端在Controller接收时用request.setCharacterEncoding(UTF-8)统一处理或者在Spring MVC里配置好CharacterEncodingFilter。我见过太多中文密码签到失败排查到最后发现是Tomcat默认编码不是UTF-8导致的。3.3 手势签到的图形坐标序列比对手势签到是四种模式里最有趣也最“轻量”的。老师在小程序端画一个手势前端把手势经过的9个点3x3网格的索引序列发送给后端学生签到时要画出相同的手势比对通过才算成功。技术实现的要点在于坐标序列的归一化。不能直接比对原始坐标因为每个人的手指粗细、画线习惯不同同一手势的原始坐标轨迹会有偏移。项目中的做法是将手势区域划分为3x3网格每个轨迹点落到网格后取其所在格子编号最后按时间顺序组成一个数字序列比如“12356987”。比对时直接比较这个序列是否一致。这种做法牺牲了一定的精度但换来了极高的比对效率——不需要任何几何算法一个字符串相等判断就完成。当然如果要做更高精度的手势匹配比如容错率更高的轨迹相似度需要引入动态时间规整DTW或编辑距离算法。但对于课堂签到这种场景严格匹配序列已经完全够用——反正手势本来就是密码画错说明就是不知道口令。前端小程序端的网格序列获取逻辑大致如下// 手势绘制完成后获取所有经过的点 const touchPoints this.data.touchPoints; const gridSequence []; const gridSize 3; for (let point of touchPoints) { const xIndex Math.floor(point.x / (canvasWidth / gridSize)); const yIndex Math.floor(point.y / (canvasHeight / gridSize)); const gridNum yIndex * gridSize xIndex 1; if (gridSequence[gridSequence.length - 1] ! gridNum) { gridSequence.push(gridNum); } }这里去掉了连续重复的格子编号因为手指在一个格子里停留时会产生大量相同坐标点不过滤的话序列被拉得很长也会影响匹配准确度。还有一个细节手势密码的长度校验。序列位数太少比如只有1位很容易被蒙对建议至少4位且不能全部相同。后端在更新手势密码时做限制防止老师设置“1111”这类毫无安全性的手势。3.4 位置签到的经纬度计算与范围判定位置签到的核心是计算学生当前位置与课堂位置的距离小于设定阈值通常是100米到200米即视为在课堂内。之所以用“课堂位置”而不是“信标”或“Wi-Fi”是因为项目定位是通用型课堂签到不能要求每个教室都部署蓝牙信标设备。GPS定位虽然在高楼密集区会有偏差但对绝大多数场景够用了。前端通过wx.getLocation获取经纬度传给学生所在位置wx.getLocation({ type: gcj02, success: (res) { wx.request({ url: https://your.domain.com/api/sign/location, data: { taskId: taskId, latitude: res.latitude, longitude: res.longitude } }); } });注意这里的type: gcj02是国测局坐标而老师设置课堂位置时如果从高德地图取到的坐标本身也是gcj02两边就能直接计算。如果从其他来源比如GPS原始坐标获取的坐标混用了坐标系计算出来的距离偏差可能达到几百米这会导致签到误判。后端用Haversine公式计算球面两点距离private static final double EARTH_RADIUS 6371000; private double getDistance(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) )); return s * EARTH_RADIUS; }把这个计算封装成单独的工具类避免在Service层里直接写裸公式既方便单元测试也方便未来扩展其他距离算法。阈值参数最好在系统配置表里维护而不是硬编码在代码里因为不同建筑大小、GPS信号强度的差异需要老师或管理员灵活调整。位置签到最大的坑在于定位权限。iOS用户如果拒绝了定位权限wx.getLocation会走fail回调。这种情况下要给出友好的提示并引导用户去“设置-授权管理”里重新开启。另外部分安卓机型的GPS信号在室内极弱定位结果会漂移到几百米外导致明明人在教室里却签到失败。这个问题的应对方案在后文“常见问题”里再展开。4. SSM后端的核心接口设计与实现4.1 登录认证与身份识别机制微信小程序的登录流程是前端调用wx.login获取临时code传给后端后端通过code向微信服务器换取openid然后根据openid去查用户表查到就返回自定义登录态token查不到就跳转到绑定页面完成手机号/学号绑定。这里的token方案比较老派但足够可靠。登录成功后生成一个32位UUID存到Redis里key为token:uuidvalue为user_id每次请求在Header里带上token后端拦截器根据token查Redis拿到用户身份。如果不想引入Redis也可以用数据库表存token并记录过期时间但并发高时会有性能瓶颈。接口统一返回结构的定义一定要规范前端才能少写很多分支判断。我的习惯是{ code: 200, message: success, data: { } }code为200表示成功其他值表示各种失败原因。前端在wx.request的success回调里统一判断code为200才继续处理业务逻辑。这个约定在项目文档的第一章就写清楚前后端联调时能省掉大量沟通成本。4.2 签到核心接口的Request与Response设计以限时签到为例接口设计如下POST /api/sign/time Request: { taskId: 12 } Response: { code: 200, message: 签到成功, data: { signId: 566, signTime: 2024-06-01 09:12:33 } }密码签到多一个password字段位置签到多latitude和longitude字段手势签到多一个gesture序列字段。所有签到接口都放在SignController里每个方法只做参数校验和调用Service业务逻辑全部下沉到SignService。这样做的好处是测试可以直接对Service层写单元测试不依赖Web环境。Controller层示例RestController RequestMapping(/api/sign) public class SignController { Autowired private SignService signService; PostMapping(/time) public Result signByTime(RequestBody SignTimeRequest request) { return signService.signByTime(request); } }这样的代码结构看看方法名就能知道接口的功能对后续接手维护的人非常友好。4.3 Spring MVC拦截器实现登录校验项目中所有的签到、查询接口都需要登录拦截。Spring MVC里实现一个HandlerInterceptor在preHandle方法中校验tokenpublic class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String token request.getHeader(Authorization); if (token null || !RedisUtil.exists(token: token)) { response.setStatus(401); return false; } // 从Redis获取userId放入ThreadLocal方便后续使用 UserContext.setUserId(RedisUtil.get(token: token)); return true; } }在Spring配置文件里注册拦截器并排除login、register等公开接口mvc:interceptors mvc:interceptor mvc:mapping path/api/**/ mvc:exclude-mapping path/api/user/login/ mvc:exclude-mapping path/api/user/bind/ bean classcom.example.interceptor.LoginInterceptor/ /mvc:interceptor /mvc:interceptors这里用UserContext存储当前登录用户ID是在单机部署下最简方案。等以后用户量大了再做多节点部署时就要把用户上下文换成基于Redis的分布式Session方案了。5. 项目部署与微信小程序联调实操5.1 本地环境搭建的完整步骤从零跑起这个项目核心步骤就五步但每一步都有细节坑。第一步是安装并配置JDK 8与Maven。JDK版本建议用8SSM项目在JDK 8上最稳用11或17会有各种JAXB和反射相关的兼容问题。Maven配置好阿里云镜像源否则下载依赖能等十分钟。第二步是安装MySQL并导入项目SQL脚本。启动MySQL服务后先建库再导入mysql -u root -p sign_system.sql如果SQL脚本编码不对导致中文乱码用--default-character-setutf8mb4参数重新导入。第三步是修改后端配置文件的数据库连接信息。项目里的jdbc.properties改成你自己的MySQL账号密码jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/sign_system?serverTimezoneAsia/ShanghaiuseSSLfalsecharacterEncodingutf8allowPublicKeyRetrievaltrue jdbc.usernameroot jdbc.passwordyourpasswordallowPublicKeyRetrievaltrue一定要加MySQL 8.0默认的认证插件是caching_sha2_passwordJDBC首次连接时需要这个参数才能正确处理公钥检索否则报Public Key Retrieval is not allowed错误。第四步是启动Tomcat。把项目打成WAR包放到Tomcat的webapps目录下启动后访问http://localhost:8080/sign-system/看到提示页说明Web层起来了。第五步是微信小程序端准备。用微信开发者工具导入小程序源码把app.js中后端接口地址从线上域名改为本地局域网IP。这里要注意小程序开发者工具里勾选“不校验合法域名”否则会被安全策略拦下。5.2 真机调试时后端接口访问不通怎么办微信开发者工具中的“不校验合法域名”选项只在工具里生效真机上必须使用HTTPS地址。所以真机调试前你至少需要一个满足以下条件的后端地址公网可访问、已备案域名、配置HTTPS证书。如果是课程设计或演示最简单的方式是用内网穿透工具把本地Tomcat映射到公网绑上自己的域名和免费SSL证书。不建议用默认分配的二级域名因为微信要求request合法域名必须是备案过的而且最终审核可能看域名主体信息。后端还要配置CORS跨域。小程序不是浏览器环境理论上不受跨域限制但如果你在微信开发者工具里调试时用wx.request访问本地局域网IP开发工具会模拟浏览器环境此时后端要允许跨域response.setHeader(Access-Control-Allow-Origin, *); response.setHeader(Access-Control-Allow-Methods, GET, POST, PUT, DELETE, OPTIONS); response.setHeader(Access-Control-Allow-Headers, Content-Type, Authorization);如果小程序端请求报“url not in domain list”就是域名没有配置好。登录微信公众平台在“开发管理-开发设置-服务器域名”中添加https://yourdomain.com为request合法域名。5.3 SSM项目打包与部署的注意事项项目用Maven打包时要注意配置文件是否被正确打进WAR包。Maven默认会把src/main/resources下的配置打包到WEB-INF/classes下如果你的Tomcat端口号不是默认的8080或配置了虚拟目录需要在部署时同步修改。用IDEA开发时一个常见问题是“本地运行正常但部署到Tomcat后页面404”。排查思路一般是先检查WAR包名字是否正确再检查Tomcat的conf/server.xml中Context的路径是否匹配最后看Tomcat的logs/catalina.out日志确认Spring容器是否成功初始化。其中90%的问题出在数据库连接失败导致Spring容器启动中断所以排查时优先确认MySQL远程访问权限是否开启。MySQL默认只监听localhost如果后端和数据库不在同一台机器上需要给用户授权远程访问GRANT ALL PRIVILEGES ON sign_system.* TO root% IDENTIFIED BY yourpassword; FLUSH PRIVILEGES;6. 常见问题与排查技巧实录6.1 位置签到在室内定位漂移严重GPS在室内的定位精度会从10米掉到100米以上这是硬伤。如果你实测发现签到距离误差过大可以考虑用微信的Wi-Fi定位能力替代。wx.getLocation在高精度模式下会自动融合Wi-Fi和基站信号但前提是用户打开了Wi-Fi开关。在请求定位时加上isHighAccuracy: true参数wx.getLocation({ type: gcj02, isHighAccuracy: true, highAccuracyExpireTime: 4000, success: (res) { /* 使用高精度定位结果 */ } });另外把签到半径阈值从默认的100米放宽到200米也是缓解假阴性的常用手段。放宽会影响防作弊强度建议在系统设置里做成课程级别的可配置项由老师根据教室实际情况自行调整。6.2 限时签到时学生改本地时间绕过限制这个问题的英文术语叫“本地时间欺骗”。学生在手机上把系统时间改到签到时间窗口内如果前端直接用本地时间判断就能绕过限制。前面已经强调了后端必须用服务器时间做最终校验这里再补充一个防御技巧前端在启动签到页面时从后端获取一次服务器时间戳用服务器时间 本地已运行时间作为显示的倒计时基准避免直接读本地时间。不过这个方案也不是万无一失——如果用户改时间后强行杀掉小程序清理本地存储再打开前端缓存也会被重置。所以核心结论不变安全校验永远依赖后端前端做多少校验都只是改善用户体验。6.3 密码签到时中文口令导致乱码这是我在联调阶段真实遇到的问题。老师在后台设置“上课啦”作为口令学生端怎么输入都提示密码错误。排查过程是这样的先看后端日志发现接收到的参数变成了“上课啦”这是典型的UTF-8字节被当成ISO-8859-1解码的结果。解决方案是在Tomcat的server.xml中给Connector添加URIEncodingUTF-8Connector port8080 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 URIEncodingUTF-8 /如果用了Spring MVC的RequestBody接收JSON那只需要保证前端Content-Type: application/json;charsetUTF-8即可不乱码。问题主要出现在POST表单或GET查询参数场景用RequestParam接收时容易被Tomcat默认编码坑。6.4 小程序端canvas绘制手势在iOS上不渲染手势签到前端用canvas实现但部分iOS小程序版本上canvas绘制内容不显示。这个问题通常是因为canvas的type属性指定为2d后在iOS端需要等canvas初始化完成再绘制。用wx.createSelectorQuery().select(#gestureCanvas).fields({ node: true, size: true })获取画布节点然后调用ctx.beginPath()时注意ctx是否为空。另一个坑是canvas的touchmove事件回调频率过高导致画布闪烁或卡顿。解决办法是设置一个节流标志两次绘制之间至少间隔16mslet lastDrawTime 0; function onTouchMove(e) { const now Date.now(); if (now - lastDrawTime 16) return; lastDrawTime now; // 执行绘制逻辑 }6.5 签到记录查询慢大数据量下索引优化跑了一个学期后t_sign_record表十几万条数据老师查“某次签到谁没到”变得很慢。排查后发现查询条件里用了task_id user_id但这两列没有建索引导致每次全表扫描。解决方案是给高频查询字段加上组合索引ALTER TABLE t_sign_record ADD INDEX idx_task_user (task_id, user_id); ALTER TABLE t_sign_record ADD INDEX idx_user_time (user_id, sign_time);加完索引后查询从秒级降到了毫秒级效果立竿见影。另一个建议是对t_sign_record按学期做分区或归档历史数据分离出去后在线表体积大大减少查询自然更快。6.6 SSM框架常见配置错误速查我把项目开发中遇到的SSM配置错误整理成一个速查表遇到类似报错可以直接对照。错误提示可能原因排查方向Invalid bound statement (not found)Mapper.XML没有被扫描到检查MyBatis的mapperLocations配置是否指向了classpath:mapper/*.xmlFailed to configure a DataSource数据库连接配置错误检查jdbc.properties中URL、账号、密码是否一致尝试用命令行工具连接测试Consider defining a bean of type MapperMapper接口没加Mapper或没扫描Spring配置中加入MapperScannerConfigurer或用MapperScan指定包路径javax.servlet.ServletException: Circular view pathController返回字符串和视图解析器冲突检查ResponseBody或RestController是否正确使用Access denied for user rootlocalhostMySQL账号权限不足用root登录后用GRANT授权7. 项目结构说明与二次开发建议7.1 源码目录结构的阅读指南整个项目的目录结构分三块前端小程序代码目录、后端SSM工程代码目录、数据库脚本目录。拿到源码后建议按这个顺序阅读先看数据库脚本理解表关系。再看后端的SignController了解接口清单然后顺藤摸瓜看SignService和对应的Mapper.XML。最后阅读小程序的pages/teacher和pages/student下各自的签到页面把后端接口和前端按键逐一对应。后端工程的包结构是标准的com.example.signcom.example.sign ├── controller # Controller层接收前端请求 ├── service # 业务层处理具体逻辑 │ └── impl ├── mapper # MyBatis的Mapper接口 ├── entity # 与数据库表对应的实体类 ├── common # 通用类如返回结果封装、常量定义 ├── config # Spring配置类 └── utils # 工具类如距离计算、JWT生成前端小程序目录里的pages文件夹teacher子目录是老师端页面发起签到、查看记录、创建课程student子目录是学生端页面签到、查看出勤。utils目录里封装了所有网络请求方法统一在request.js中管理。7.2 从课程设计到生产级系统需要补什么这个项目作为课程设计或毕业设计已经非常完整但如果要真正应对一个学院上千人的日活还有几个方向需要补强。第一个是消息推送。目前学生端看到签到任务需要主动刷新页面如果能结合微信小程序的订阅消息老师发起签到时向学生推送一条模板消息签到率会明显提升。实现上需要后端调用微信的subscribeMessage.send接口并提前申请到有权限的模板ID。第二个是并发控制。当几百人同时发起签到请求时Tomcat默认线程池可能会被打满。可以在server.xml中调大maxThreads同时在数据库连接池Druid或HikariCP中配置合理的最大连接数避免数据库连接成为瓶颈。第三个是数据可视化。当前端和后端的数据已经完整的前提下可以借用ECharts绘制一个出勤率趋势图按周展示每个学生的到课情况。不过这些属于锦上添花对签到系统的核心功能没有任何影响。7.3 几种签到模式的组合使用场景推荐根据我的实际体验不同课程类型适合不同的签到策略大班理论课推荐“限时 位置”双重校验。限时解决代签位置确保人在教学楼范围内。小班研讨课推荐手势签到。节奏快、趣味性强不至于在上课开始时用密码签到显得太生硬。实验课/机房课推荐密码签到。因为机房信号屏蔽严重、GPS定位不准密码是最高效的方式。体育课/户外课推荐位置签到但半径阈值要调大一些因为户外GPS信号好误差小。组合策略的实现不难只需要在发起签到任务时允许多选签到类型后端在SignService中按规则依次校验即可。我在自己的版本里扩展过这个功能核心逻辑就是先判断当前任务启用了哪些签到方式然后逐项校验全部通过才写入签到记录。8. 我踩过的几个真实项目坑写到这里我再分享几个这个项目开发过程中印象最深的坑。第一个是数据库连接池选型。SSM项目教学模板大多用DBCP连接池但DBCP在高并发下容易出现连接泄露表现为一个学期后系统越来越卡重启Tomcat又恢复正常。建议改成Druid连接池它自带监控页面能直接看到每个接口的SQL执行时间和连接池使用情况排查问题效率极高。第二个是前端请求封装不统一导致的调试噩梦。项目刚开始时每个页面都自己写wx.request调用接口时有的传JSON格式、有的传表单格式后端Controller参数接收方式也各不相同联调时天天出问题。后来我把所有网络请求统一封装到request.js中实现GET、POST、PUT、DELETE四个方法所有调用统一走BaseUrl path一个入口问题立刻少了一大半。第三个是定位组件的隐私合规。2023年后微信小程序官方要求涉及用户信息的API必须在app.json中声明对应权限用途并在页面中做引导说明。如果直接在代码里调用wx.getLocation而不做任何说明文案配置审核会被驳回。好在项目里的隐私弹窗处理模块经过多次迭代已经形成了标准范式首次进入签到页时弹窗说明定位用途和授权方式用户拒绝后再进入时会引导到设置页重新授权。最后一个建议是拿到这个项目源码后不要急着打开微信开发者工具去跑。先花一小时看项目和文档把表结构和接口流程走一遍再动手部署。磨刀不误砍柴工有了整体认知后调试和二次开发都会顺畅得多。如果过程中遇到文档里没写清楚的地方优先去看后端的日志输出——所有关键业务节点都有详细的日志打印这是排查问题最直接、最可靠的抓手。本文还有配套的精品资源点击获取
返回列表