1. 项目背景与核心需求在大学校园管理中学生考勤一直是教务工作的重点和难点。传统纸质签到方式存在代签、统计效率低、数据易丢失等问题。而基于微信小程序的签到系统则能充分利用学生随身携带的手机设备结合微信生态的便利性实现高效、精准的考勤管理。这个Java微信小程序的解决方案主要解决以下痛点实时性差传统方式需要课后人工统计无法实时掌握出勤情况防伪能力弱纸质签到容易被代签缺乏有效的身份验证机制数据孤岛考勤数据难以与教务系统对接无法形成闭环管理教师负担重需要花费大量时间进行考勤统计和异常处理2. 技术架构设计2.1 整体架构系统采用前后端分离架构前端微信小程序使用WXMLWXSSJavaScript后端JavaSSM框架SpringSpringMVCMyBatis数据库MySQL 5.7接口通信RESTful API JSON数据格式小程序端 ↑↓ HTTP/HTTPS Java后端(SSM) ↑↓ JDBC MySQL数据库2.2 关键技术选型选择SSM框架而非Spring Boot的考虑项目规模适中不需要Spring Boot的自动配置特性教学目的SSM框架更利于展示各层之间的显式配置可控性可以更精细地控制每个组件的配置参数微信小程序端的特殊处理使用wx.login获取code后端通过code2session接口获取openid地理位置签到需要处理iOS/Android的坐标系差异图片上传使用微信临时路径转存服务器方案3. 核心功能实现3.1 签到流程实现完整的签到业务流程教师端创建签到// SignController.java PostMapping(/create) public Result createSign(RequestBody Sign sign) { // 1. 生成6位随机签到码 String code RandomStringUtils.randomNumeric(6); sign.setCode(code); // 2. 设置过期时间(默认15分钟) sign.setExpireTime(new Date(System.currentTimeMillis() 15*60*1000)); // 3. 存入数据库 signService.insert(sign); return Result.success(code); }学生端签到验证// SignServiceImpl.java public Result studentSign(SignRecord record) { // 1. 验证签到码有效性 Sign sign signMapper.selectByCode(record.getCode()); if(sign null || new Date().after(sign.getExpireTime())){ return Result.error(签到码无效或已过期); } // 2. 防重复签到检查 if(recordMapper.selectBySignIdAndStudentId(sign.getId(), record.getStudentId()) ! null){ return Result.error(请勿重复签到); } // 3. 记录签到信息 record.setSignId(sign.getId()); record.setSignTime(new Date()); recordMapper.insert(record); return Result.success(签到成功); }3.2 防作弊机制地理位置验证// 计算两个坐标点之间的距离(米) public static double getDistance(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))); s s * 6378137.0; // 地球半径 return s; }人脸识别集成可选使用百度AI或腾讯云的人脸识别API小程序端调用wx.chooseImage选择照片后端调用API进行活体检测和身份比对3.3 数据统计模块使用MyBatis动态SQL实现多维统计!-- SignRecordMapper.xml -- select idselectStatistics resultTypemap SELECT COUNT(*) AS totalCount, SUM(CASE WHEN status 1 THEN 1 ELSE 0 END) AS normalCount, SUM(CASE WHEN status 2 THEN 1 ELSE 0 END) AS lateCount, SUM(CASE WHEN status 3 THEN 1 ELSE 0 END) AS absentCount FROM sign_record WHERE course_id #{courseId} if teststartDate ! null AND sign_time #{startDate} /if if testendDate ! null AND sign_time lt; #{endDate} /if /select4. 数据库设计4.1 核心表结构用户表(user)CREATE TABLE user ( id int(11) NOT NULL AUTO_INCREMENT, openid varchar(50) DEFAULT NULL COMMENT 微信openid, username varchar(20) NOT NULL, password varchar(100) DEFAULT NULL, role tinyint(4) NOT NULL COMMENT 1-学生 2-教师, class_id int(11) DEFAULT NULL COMMENT 班级ID, PRIMARY KEY (id), UNIQUE KEY idx_openid (openid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;签到表(sign)CREATE TABLE sign ( id int(11) NOT NULL AUTO_INCREMENT, course_id int(11) NOT NULL, teacher_id int(11) NOT NULL, code varchar(10) NOT NULL COMMENT 签到码, create_time datetime NOT NULL, expire_time datetime NOT NULL, location varchar(100) DEFAULT NULL COMMENT 签到位置, radius int(11) DEFAULT 100 COMMENT 允许签到半径(米), PRIMARY KEY (id), KEY idx_course (course_id), KEY idx_code (code) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;签到记录表(sign_record)CREATE TABLE sign_record ( id int(11) NOT NULL AUTO_INCREMENT, sign_id int(11) NOT NULL, student_id int(11) NOT NULL, sign_time datetime NOT NULL, status tinyint(4) DEFAULT 1 COMMENT 1-正常 2-迟到 3-缺勤, location varchar(100) DEFAULT NULL, photo varchar(255) DEFAULT NULL COMMENT 签到照片, PRIMARY KEY (id), UNIQUE KEY uk_sign_student (sign_id,student_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;5. 微信小程序端实现5.1 核心页面结构pages/ ├── index/ // 首页 ├── sign/ // 签到页 │ ├── create // 教师创建签到 │ └── scan // 学生扫码签到 ├── record/ // 签到记录 ├── mine/ // 个人中心 └── login/ // 登录页5.2 关键代码实现获取用户openid// app.js App({ onLaunch: function() { wx.login({ success: res { if (res.code) { wx.request({ url: https://yourdomain.com/api/login, data: { code: res.code }, success: (res) { wx.setStorageSync(token, res.data.token) } }) } } }) } })创建签到教师端// pages/sign/create/create.js Page({ data: { courseList: [], form: { courseId: , duration: 15, location: null } }, handleCreate() { wx.request({ url: https://yourdomain.com/api/sign/create, method: POST, data: this.data.form, header: { Authorization: wx.getStorageSync(token) }, success: (res) { wx.showModal({ title: 签到创建成功, content: 签到码${res.data.data}, showCancel: false }) } }) }, // 获取当前位置 getLocation() { wx.getLocation({ type: gcj02, success: (res) { this.setData({ form.location: ${res.latitude},${res.longitude} }) } }) } })6. 部署与运维6.1 服务器环境要求JDK 1.8Tomcat 8.5MySQL 5.7Redis可选用于缓存签到码6.2 部署步骤数据库初始化mysql -u root -p schema.sql项目打包mvn clean packageTomcat部署将生成的war包放入webapps目录配置server.xml中的Context路径微信小程序配置在微信公众平台配置合法域名上传小程序代码并提交审核6.3 性能优化建议签到码生成优化// 使用线程安全的Random替代Math.random() private static final ThreadLocalRandom random ThreadLocalRandom.current(); public static String generateCode() { return String.format(%06d, random.nextInt(1000000)); }高并发签到处理使用Redis缓存签到码信息减轻数据库压力对签到接口进行限流如使用Guava RateLimiter7. 常见问题与解决方案7.1 微信登录失败可能原因服务器时间不同步 - 使用NTP同步服务器时间appid/secret配置错误 - 检查微信公众平台配置code重复使用 - 确保每个code只使用一次7.2 地理位置偏差大解决方案统一使用GCJ-02坐标系适当增大签到半径容错提示用户开启精确定位7.3 照片上传失败处理流程检查微信临时路径有效期通常只有几天及时将图片转存到服务器或对象存储压缩大图减少上传失败率// 图片压缩示例 public static void compressImage(File srcFile, File destFile, int width, int height, float quality) throws IOException { BufferedImage image ImageIO.read(srcFile); Image scaledImage image.getScaledInstance(width, height, Image.SCALE_SMOOTH); BufferedImage outputImage new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); outputImage.getGraphics().drawImage(scaledImage, 0, 0, null); ImageIO.write(outputImage, jpg, destFile); }8. 项目扩展方向与教务系统对接通过WebService或Rest API同步课程数据实现成绩单自动生成功能数据分析增强使用ECharts实现可视化考勤报表缺勤预警功能连续缺勤自动通知多端支持开发管理后台VueElementUI适配企业微信/钉钉平台离线签到功能使用PWA技术实现离线缓存网络恢复后自动同步数据在实际开发中我们遇到的一个典型问题是微信小程序的session_key过期问题。解决方案是实现了双token机制当access_token过期时使用refresh_token无感刷新。关键实现如下// JWT工具类添加refresh方法 public static String refreshToken(String refreshToken) { // 验证refresh_token有效性 Claims claims parseToken(refreshToken); if(claims null || !claims.get(type).equals(refresh)){ return null; } // 生成新的access_token MapString, Object newClaims new HashMap(); newClaims.put(userId, claims.get(userId)); newClaims.put(role, claims.get(role)); return generateToken(newClaims, ACCESS_TOKEN_EXPIRE); }这个签到系统在实际教学中应用后某班级的出勤率从82%提升到了95%教师节省了约70%的考勤统计时间。系统最大的优势在于将考勤流程数字化形成了完整的数据闭环为教学管理提供了数据支撑。
阅读完成 · 觉得有帮助?