ARTICLE DETAIL

资讯详情

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

微信课堂助手管理系统:小程序+Spring Boot+MySQL 全栈开发实战

微信课堂助手管理系统:小程序+Spring Boot+MySQL 全栈开发实战 微信课堂助手管理系统这个项目我从头到尾做了六周。名字听起来有点学术但真要落地它要解决的事情非常具体课堂上签到点名怎么从两分钟压缩到三十秒作业收发怎么不再靠群接龙通知公告怎么保证学生真的能看到班主任和任课老师怎么随时拿到出勤率、作业完成率这些数据。这个项目最终产出的是一个完整的微信小程序应用外加后端管理接口、数据库脚本、部署文档和毕业论文说明。无论你是准备把它当毕业设计题目还是想学习小程序前后端联调的整体流程这套东西都值得完整拆一遍。我选的技术组合是微信小程序原生前端 Spring Boot 后端 MySQL 数据库这个搭配在高校项目里出现频率最高因为它每一层都有海量资料可查而且踩过的坑基本都能在社区找到现成答案。这篇文章我把整套项目的设计思路、核心代码逻辑、数据库建模过程、开发中遇到的故障排查以及最后怎么写论文和准备答辩全部梳理出来给正在做类似系统的人一个可以直接参考的完整路线。1. 项目全景拆解课堂助手到底在解决什么问题1.1 真实课堂场景里的三个痛点先别急着写代码。我在开始动手前先去旁听了几门专业课观察老师上课的真实流程。最典型的场景是这样的课前老师拿着纸质名单点名一节课四十五分钟光点名就耗掉五六分钟台下学生低头玩手机课中布置作业课后学生在微信群里用接龙形式提交作业图片和文档混在几百条聊天记录里老师批改时要反复翻聊天记录遇到调课、补课、考试安排班长在群里发公告总有人说没看到。这三个痛点对应到系统里就是三个核心模块在线签到、作业管理、通知公告。再往深一层想老师和管理员还需要看到这些行为产生的数据比如某门课一学期的出勤曲线某位学生交作业的及时率。所以系统不能只是一个学生端打卡工具还必须有管理端数据看板。1.2 角色权限划分与功能边界系统里一共有三类角色学生、教师、系统管理员。学生通过微信登录小程序可以查看自己的课程表、进行课堂签到、接收通知、提交作业、查看作业反馈和成绩。教师通过管理端创建课程、发起签到、布置和批改作业、发布通知、查看所授课程的统计报表。管理员负责基础数据维护比如院系、班级、教师账号、课程归属并且能看到全系统的运行概览。功能边界一定要在开发前划清楚不然很容易做成一锅粥。我的经验是学生端只管自己相关的数据教师端只能操作自己创建的课程管理员不参与具体教学活动只维护元数据。这个边界直接决定了后面数据库表设计时谁对谁可见、接口鉴权时怎么校验权限。1.3 技术选型为什么是小程序加 Spring Boot很多人在选题阶段会纠结为什么不直接做 Web 端为什么不选 Vue 全家桶我的判断是这样的。课堂签到这个场景有一个天然特点学生必须带着手机在教室里才能完成真实签到。微信小程序不需要额外安装 App扫码即用而且微信提供了完整的定位授权、蓝牙、二维码识别、订阅消息推送能力刚好覆盖签到和通知这两个核心功能。对用户来说零安装成本对开发者来说微信生态的登录和鉴权已经帮你解决了大部分认证问题。后端选 Spring Boot 而不是 Node.js 或 Django核心原因有三条。第一这个项目最终要写论文Spring Boot 的 MVC 架构、三层分层的代码结构在论文里非常容易讲清楚评审老师一眼就能看懂。第二Spring Boot 加 MyBatis 是国内企业级应用最主流的组合毕设完成后往下延伸找工作也顺路。第三完整的教学资料最多社区案例最丰富不管是依赖冲突还是连接池问题几乎都能搜到中文解决方案。数据库用 MySQL 是顺理成章的它是关系型数据库里最普及的选择InnoDB 引擎在事务和并发控制方面对于这种中小规模的校园应用完全够用。整个项目没有引入 Redis 做缓存也暂时没上微服务因为从项目规模来说并不需要硬上反而会让论文的复杂度失控。2. 小程序端设计登录、请求、数据缓存一个都不能少2.1 微信登录和手机号获取的正确姿势小程序端的第一个技术点是登录。按微信官方推荐流程小程序通过 wx.login 获取临时 code后端拿着 code 调用微信的 code2Session 接口换 openid 和 session_key。这里要注意一个容易踩的坑临时 code 五分钟内有效且只能使用一次所以整个流程必须是前端把 code 发给后端由后端统一处理换 session 的逻辑前端不应该也没有能力直接解析 session_key。代码结构大概是这样的前端在小程序启动后调用登录接口// app.js 或工具库 login.js const login () { return new Promise((resolve, reject) { wx.login({ success: async (res) { if (res.code) { // 将 code 发送到后端 const data await request.post(/api/auth/login, { code: res.code }); // 后端返回自定义 token wx.setStorageSync(token, data.token); resolve(data); } else { reject(res); } }, fail: reject }); }); };后端收到 code 之后依次做三件事调微信接口换 openid、查数据库确认用户是否存在、不存在则自动创建一条新用户记录。最终返回给前端的不应该是 openid而应该是系统自己生成的 tokentoken 里通过业务字段标记用户角色。这样做的原因很简单openid 是微信侧的敏感标识每次请求都带它既不安全也不方便权限校验。关于手机号授权这里重点提醒各位。微信从基础库 2.21.2 开始调整了获取手机号的规则现在必须使用 button 组件并设置 open-type 为 getPhoneNumber然后监听 bindgetphonenumber 回调拿到加密数据后由后端解密获取真实手机号。以前那种 wx.getPhoneNumber 的方式已经废弃了。另外手机号授权需要小程序完成微信认证个人主体的小程序是没有这个能力的所以做毕设时尽量让导师提供一个企业主体的 AppID或者做好用测试号的备案准备。2.2 顶部导航栏高度适配小程序开发里一个非常细节但很多人栽跟头的地方是顶部导航栏的适配。微信小程序的导航栏分两种默认导航和自定义导航。默认导航在不同型号手机上高度不一致如果你是做自定义导航栏就需要动态计算状态栏高度和胶囊按钮的上下间距否则会出现按钮被刘海屏遮挡的情况。获取胶囊按钮位置信息的官方 API 是 wx.getMenuButtonBoundingClientRect配合 wx.getSystemInfoSync 获取状态栏高度可以算出导航栏的自适应高度。我在项目里封装了一个工具函数export const getNavBarInfo () { const system wx.getSystemInfoSync(); const menu wx.getMenuButtonBoundingClientRect(); const statusBarHeight system.statusBarHeight || 20; const navBarHeight menu.height (menu.top - statusBarHeight) * 2; return { statusBarHeight, navBarHeight, menu }; };这个函数在首页、签到页、详情页都要用建议放在公共模块里统一调用。如果你为了省事直接用默认导航栏体验上问题不大但整体界面的定制程度会低很多特别是想做首页顶部一个醒目的彩色背景时默认导航栏会非常突兀。2.3 网络请求封装把 wx.request 包装成 Promise 用原生 wx.request 不支持 Promise而实际开发中几乎所有接口调用都需要异步处理。我建议在项目一开始就封装一个统一的请求模块不要到后期再补。封装的原则是统一处理基础 URL、token 注入、返回码判断、错误统一提示。一个可以在生产里直接用的请求封装逻辑可以参考下面这个结构。const request (options) { return new Promise((resolve, reject) { const token wx.getStorageSync(token); wx.request({ url: https://api.example.com${options.url}, method: options.method || GET, data: options.data || {}, header: { Content-Type: application/json, Authorization: Bearer ${token} }, success: (res) { if (res.statusCode 200 res.data.code 0) { resolve(res.data.data); } else if (res.statusCode 401) { // token 失效跳转登录 wx.navigateTo({ url: /pages/login/index }); reject(res); } else { wx.showToast({ title: res.data.message || 请求失败, icon: none }); reject(res); } }, fail: (err) reject(err) }); }); };要注意这里我用了 https 开头的完整域名。微信小程序在生产环境强制要求接口必须是 HTTPS 并且经过备案的域名同时要在小程序后台配置 request 合法域名。本地开发时可以在开发者工具里勾选“不校验合法域名”但真机调试是绕不过这个限制的。2.4 列表分页加载与课程列表实现课堂助手系统中课程列表、作业列表、签到记录都是典型的列表页。如果想只做前端截断只展示前十条数据那确实写起来快但用户体验极差而且评审老师一问“如果数据量超过一百条怎么办”你就很难自圆其说。正确的做法是后端接口支持分页参数 pageNum 和 pageSize前端在小程序里监听页面触底事件 onReachBottom加载下一页时把新数据追加到当前列表中。前端实现的关键点在于维护一个分页状态对象Page({ data: { list: [], page: 1, pageSize: 10, hasMore: true, loading: false }, async onReachBottom() { if (!this.data.hasMore || this.data.loading) return; this.setData({ loading: true }); const res await request({ url: /api/course/list, data: { page: this.data.page, pageSize: this.data.pageSize } }); this.setData({ list: this.data.list.concat(res.records), page: this.data.page 1, hasMore: res.total this.data.page * this.data.pageSize, loading: false }); } });这里还有一个体验细节如果当前页数据不足一屏onReachBottom 永远不会触发用户会以为加载坏了。建议在 onReady 后判断一次 list 的长度如果小于 pageSize 就自动把下一页拉回来。3. 后端管理系统的设计与数据库建模3.1 表结构怎么设计才不容易返工数据库是整个项目中返工率最高的部分。建模如果前期想不清楚后期加一张表就要连带着改一堆接口和页面。这个系统的核心表我最终确定为九张用户表、角色表、院系列表、班级表、课程表、选课关系表、签到记录表、作业表、通知公告表。用户表里除了账号、姓名、头像还要存微信 openid、手机号、角色标识以及所属班级的 ID。这里有一个非常容易犯的错误把学生、教师、管理员设计成三张独立的表。千万不要这么做三种角色只是同一类“用户”的不同属性用一张表加一个 role 字段区分另外用外键关联班级会让后面所有涉及权限判断的逻辑都简洁得多。签到记录表的核心字段包括签到课程 ID、学生用户 ID、签到时间、签到状态正常/迟到/缺勤、签到方式定位/二维码/手势、签到地理位置。这个表是典型的流水型数据数据量增长快论文里可以重点写索引优化策略。我建表时加了这样几个索引ALTER TABLE attendance_record ADD INDEX idx_course_time (course_id, sign_time), ADD INDEX idx_student_course (student_id, course_id);这两个索引对应两类最常见的查询按课程查某次签到名单按学生查某门课的所有出勤记录。如果不加索引数据几百条时感觉不到问题但一个学期下来一门课就有几千条签到记录全表扫描的速度会让人抓狂。3.2 接口按什么规范设计后端接口统一采用 RESTful 风格路径用名词复数表示资源方法表示操作。比如获取课程列表是 GET /api/courses创建课程是 POST /api/courses提交作业是 POST /api/homework/submit删除作业是 DELETE /api/homework/{id}。所有接口的返回结构统一封装格式为{ code: 0, message: success, data: {} }code 为 0 表示成功非 0 表示业务异常。这样做的好处是前端请求封装只需要判断一次 code业务错误和网络错误分开处理。我们在实际开发时约定系统级错误返回 HTTP 状态码比如 401 未登录、403 无权限、500 服务器异常业务级错误返回 HTTP 200 加上非零的 code。比如“该课程已签到过”这种提示就是典型的业务错误HTTP 状态码不必是 400只要 code 非 0 即可。3.3 登录鉴权与 token 过期处理用户每次带着 token 来请求接口后端通过拦截器统一校验。我的做法是在 Spring Boot 里写一个 HandlerInterceptor拦截所有 /api/ 路径下的请求除了登录、注册、刷新 token 这类白名单接口其余一律校验 Authorization 头。token 的生成我用了 JWT载荷里放 userId 和 role过期时间设置为七天。这里有一个我在实际项目里优化过的细节小程序端每次启动都会静默调用 wx.login所以七天过期是合理的。但如果学生在签到过程中 token 突然过期体验就断掉了。所以我在前端封装了一个逻辑当请求返回 401 时先自动调用刷新接口拿到新 token然后重放原请求而不是直接粗暴地跳转登录页。这个“重放”逻辑实现时要注意请求封装里需要把重试的标识带上防止刷新 token 的接口返回 401 后陷入死循环。3.4 文件图片上传的完整链路作业提交、头像上传、签到照片这些功能都需要文件上传能力。小程序的 wx.uploadFile 会把文件以 multipart/form-data 形式提交到后端后端接收后有两条路可以走存本地磁盘或者存云存储。毕设项目我建议直接存本地磁盘的指定目录然后在数据库里只保存文件的访问路径。需要特别注意的是文件路径的安全性问题。实际生产环境里不能直接让用户访问后端磁盘路径。最好在 WebMvcConfigurer 里配置一个虚拟路径映射把外网访问的 /files/ 映射到磁盘的 upload/ 目录。同时要对上传文件做格式和大小校验图片只允许 jpg、png大小限制在 5MB 内并且对文件名做 UUID 重命名避免出现中文名、非法字符名造成的访问异常。这一点在论文的“系统安全性设计”章节里非常加分。4. 核心课堂功能模块的实现实录4.1 签到功能定位、二维码、手势码三种方案对比签到是这个系统的核心模块也是当时我在设计上反复论证的部分。常见的方案有三种。第一种是基于地理位置签到。学生端通过 wx.getLocation 获取经纬度上传到后端后端计算与教师设置的签到位置之间的距离小于某个阈值就算签到成功。这种方案最贴近“人到教室才算到”的真实场景但有一个潜在问题iOS 和安卓定位精度不一致宿舍楼靠近教学楼也容易误判而且模拟器里可以手动修改定位作弊防不住。第二种是二维码签到。教师端在发起签到时生成一个有时效性的二维码学生扫一扫完成签到。二维码背后绑定的是一个签到活动 ID每三十秒刷新一次。这种方案的优点是交互快不容易误操作缺点是学生把二维码拍照发给室友后校外也能签到。第三种是手势码签到。教师端生成一个四位数字手势码投影在屏幕或公布在课堂学生在小程序里输入后完成签到。这种方案对签名外的场景更好用可视作二维码的低成本替代。我最终的做法是同时支持定位签到和手势码签到教师端创建签到活动时选择模式并把签到时限一并设置。如果教师选择定位模式后端在签到前先范围校验签到结束后统一切入禁签状态。这样既满足了论文里对比论证不同签到公平性的需求也让答辩时的演示场景更丰富。4.2 作业模块学生上传、教师批改的闭环流程作业模块的状态机设计是整个项目里最能体现工程思维的环节。一份作业在一个学生视角里可能经历的状态有未提交、已提交待批改、已批改、已退回修改。教师视角里则是布置中、收集中、批改中、已结束。这两个状态机不是同一个东西后端设计时必须分开维护。学生的提交和撤回流程要设计好一个关键逻辑教师开始批改之后学生端就不能再撤回或重新提交了。我通过在作业记录表里加一个 lock_by_teacher_time 字段解决批改开始时由教师端调用一个批改锁定接口。这个细节在答辩时能成为你系统逻辑严谨性的有力证据。上传代码时要注意 wx.uploadFile 不支持自定义请求头所以 token 无法放到 header 里。常规解法是把 token 作为表单字段一起上传后端在处理 multipart 请求时再从参数中提取 token 做鉴权。这个坑在几乎所有涉及上传的项目里都会遇到提前知道能省很多调试时间。4.3 通知公告与订阅消息推送通知模块可以分成两部分一部分是小程序内展示的站内公告列表另一部分是微信订阅消息推送。站内公告简单表结构包含标题、内容、发布人、发布时间、接收范围。订阅消息则要复杂得多因为微信规定订阅消息是“一次性订阅”用户点击授权后只能给用户发送一条模板消息。这意味着如果教师发了十条公告你不能给每个学生连发十条订阅消息因为授权机会用完了。解决方法是授权时机控制。在教师发布公告时前端提示未订阅学生进行一次授权成功后再推送而当天已授权过的用户后台记录授权池待次日推送时再优先消耗。这个方案并不能做到百分之百送达但论文中可以把它作为“消息触达率优化策略”来写。还有一个注意点订阅消息的模板需要在微信公众平台申请审核通过后才有模板 ID个人主体通常没有权限申请所以这又是一个需要用企业主体 AppID 的功能。4.4 管理端页面给老师和管理员的控制台管理端我并没有做成一个独立的小程序而是按角色在小程序里渲染不同页面。学生在“我的”页看到的是成绩和自己所修课程教师看到的是所授课程列表、签到详情、作业批改入口管理员看到的是全校用户列表、课程统计和系统运行数据。这种“一个前端多套视图”的思路比做两个小程序要省力得多。实现方式是根据登录返回的角色字段动态配置首页底栏的 tabBar。tabBar 在原生小程序里的配置是静态的想要根据角色动态变化就不能用系统 tabBar而是自己做一个自定义 tabBar 组件。把原来的 tabBar 文件从 app.json 里移除在需要展示底栏的页面引入组件并把数据从全局 store 中读取。管理端的课程统计页面是我个人认为整体系统里最有亮点的模块。我用 Canvas 2D 绘制了出勤率周趋势图和作业提交率柱状图没用第三方图表库因为小程序包体积有限而且 Canvas 基本绘制对论文里“可视化实现”也是一个加分项。5. 开发中撞过的墙常见问题与排查记录5.1 域名白名单和 HTTPS 验证失败小程序真机调试时最常遇见的报错是“request:fail url not in domain list”。问题根源在于小程序后台只允许访问配置过的合法域名且要求 HTTPS 证书有效。如果毕设项目没有买域名可以在本地开发环境跑一个内网穿透服务把本地后端的 8080 端口映射到一个外网 HTTPS 域名再把域名填到小程序后台的开发设置里。但要注意这种方式只适合真机联调正式发布时必须换成正规的备案域名和有效证书。我在这个阶段浪费了整整一个晚上反复确认后台配置没写错后来才发现是因为微信开发者工具的版本更新之后要求同时校验 TLS 版本本地服务用的是旧版本协议。解决办法是检查 nginx 配置把 ssl_protocols 调整为 TLSv1.2 以上。5.2 开发者工具正常、真机却白屏有段时间首页在开发者工具里渲染完美但一到真机预览就白屏。排查到最后发现问题出在使用了 ES6 的某些新语法和 CSS 特性在低版本微信客户端不兼容。基础库版本过低时部分 API 不存在页面会静默报错。排查思路比较笨但有效把问题页面里的代码逐步注释二分法定位到具体哪一行引起崩溃。后来我在 app.json 里设置了最低基础库版本 2.30.0同时避免在 WXML 里使用过于复杂的模板表达式这类问题基本不再出现。5.3 手机号 getPhoneNumber 拿不到 code这个坑出现在用企业主体进行调试时。点击手机号授权的按钮后bindgetphonenumber 回调里拿到的 e.detail 只有 errMsg没有 code。原因是 AppID 没有绑定该主体的开发者权限或者该小程序的类目权限没有开通。解决方法是确认 AppID 归属、开发者工具已登录同主体账号并在 mp 后台确认“获取手机号”权限已启用。每当你调整了权限设置建议关闭开发者工具重新编译因为很多权限配置在缓存里不会立刻生效。5.4 上传文件大小超限微信小程序规定 wx.uploadFile 上传文件大小默认限制为 10MB但如果通过 chooseMedia 选图片基础库默认压缩后的图片通常不会超过 2MB。我在实现作业上传时同时允许图片和 PDF而 PDF 文件很容易超过 5MB。解决方案是在前端选择文件时做大小判断超过限制的直接弹 toast 提示后端再做一次兜底校验防止绕过前端直接调接口。5.5 订阅消息授权池的维护订阅消息相关的隐蔽坑是每次调用 wx.requestSubscribeMessage 弹窗授权时如果用户点了拒绝下次再调同样模板还会弹窗但授权状态其实没有变化容易造成消息发送失败。正确做法是后端维护每个用户的订阅授权记录表记录用户最后一次授权时间和授权模板类型前端在需要推送时检查数据库如果该用户今天已经消耗过授权就用站内信兜底通知。5.6 数据库连接池被请求打满开发阶段我发现签到高峰期接口响应变得很慢报错信息是“HikariPool-1 - Connection is not available, request timed out after 30000ms”。定位后发现是测试阶段反复重启后端连接池连接数没有释放。实际解决办法是调整 HikariCP 配置将 maximum-pool-size 设为 20最小空闲连接设为 5并给查询接口补充连接释放的日志。这类问题在论文的“系统性能优化”章节里可以做一个很好的故障分析案例。6. 项目交付规范与论文写作要点6.1 源码工程的目录结构无论你是自己留档还是准备分享源码目录结构一定要清晰。我最终的源码工程长这样classroom-assistant/ ├── backend/ │ ├── src/main/java │ ├── src/main/resources │ └── sql/classroom.sql ├── miniprogram/ │ ├── pages/ │ ├── components/ │ ├── utils/ │ └── app.js ├── docs/ │ ├── 需求文档.md │ ├── 接口文档.md │ └── 部署说明.md └── README.mdREADME 里至少要有项目简介、技术栈、如何导入数据库、如何启动后端、如何导入小程序、如何配置合法域名这几部分。很多项目分享出来别人跑不起来绝大多数原因都在于 README 写得含糊。我在部署说明里添加了一个常见问题列表包括端口占用、数据库连接参数、微信 AppID 替换位置这些信息能帮你省掉大量答疑时间。6.2 论文的章节布局与写作节奏论文部分如果你是按学校模板来写核心章节基本是绪论、相关技术介绍、系统需求分析、系统设计、系统实现、系统测试、总结。相关技术介绍尽量不要写成名词解释的罗列要结合项目说明你选它的原因。比如介绍 Spring Boot 时可以加一句“其内置 Tomcat 简化部署流程降低了环境配置复杂度”。系统测试部分不要只写功能测试用例表最好补充性能测试或者兼容性测试的数据。我当时用 WeTest 做了主流机型的兼容遍历输出了一组机型适配结论这在论文里很有说服力。6.3 答辩现场被问多的三个问题第一是“签到怎么防止代签”。答辩时要坦诚说明定位和时效性限制能做到的边界然后补充说明当前方案已经封堵了二维码转发、开了模拟定位识别。第二是“为什么选择微信原生而不是 uni-app 或 Taro”。回答角度是项目需求和原生 API 覆盖度核心功能都依赖微信原生能力跨端框架反而会引入一层转换损耗。第三是“系统并发量能支撑多少”。从数据库连接池配置和签到接口耗时数据去估算有理有据比凭空给数字更可信。我在实际答辩中被追问最多的是数据安全相关的问题比如用户手机号这种敏感信息是如何存储的。后来我在论文里专门增加了一段说明手机号解密只发生在后端数据库字段使用 AES 加密存储前端不会保存手机号明文最终这个设计也成了答辩评分的一个加分点。这个项目给我的最大收获是真正理解了“完整交付一套系统”和“写完一段能运行的代码”之间的巨大差距。从需求梳理到数据库建模从前端交互到后端鉴权从开发工具里的调试到真机上的兼容性验证再到最后论文里的文字表达每一个环节都在逼着你对这个领域的知识做串联。如果你正在考虑做一个类似的小程序毕设项目我强烈建议不要跳过需求分析阶段哪怕只是像我去旁听几节课观察真实场景也比自己闷头画原型要来得多。
返回列表