ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

微信小程序考试信息报名系统毕设全流程实战:从需求到答辩

2026/10/3 15:00:14 拓冰建站 浏览量
微信小程序考试信息报名系统毕设全流程实战:从需求到答辩 每年毕业季我都会收到一批私信问的最多的就是“毕设选题选什么”。如果你正盯着“基于微信小程序的考试信息报名系统”这个题目犹豫我直接说结论这题能做而且非常适合作为毕业设计。它既有完整的业务闭环——考生查考试、在线报名、管理员审核、状态通知又能把微信生态的登录、订阅消息、真机调试这些点全部串起来技术栈完整、演示效果好答辩时也容易说清楚。不过这个题目看着简单真正动手之后才发现坑不少我去年从需求梳理到上线演示前后花了将近三个月这篇文章就把整个项目的完整经验拆开讲给你从需求边界到技术选型从数据库设计到微信生态适配再到答辩现场怎么发挥一条线聊完。不管你是打算自己造轮子还是想抄一份足够扎实的参考这篇都能帮你少走一段弯路。1. 需求拆解考试信息报名系统到底要做哪些事很多同学拿到这个题目第一反应是打开微信开发者工具开始写页面这是最危险的起手式。我在做项目的第一天犯过同样的错误光一个“考试分类”就改了四版因为根本没想清楚这个系统服务于谁、流程在哪结束。先把需求想明白后面写代码的速度会快很多。1.1 三种角色与一条完整的报名闭环考试信息报名系统核心角色只有三类考生、系统管理员、发布考试的老师有些学校让管理员兼任。别小看这个角色划分它直接决定你的页面权限和后端接口设计。我用一张图把流程定下来这里只画逻辑不依赖任何工具老师/管理员在后台发布一场考试包括考试名称、报名开始时间、截止时间、考试地点、报名条件比如学历要求、专业限制、剩余名额考生在小程序端刷到考试列表点进详情看完报名规则后在线提交报名信息管理员在后台看到新报名审核通过或驳回考生在小程序里查到自己的报名状态。到这里一个完整的业务闭环就结束了。之所以要在这个系统里加入“审核”这个环节而不是报名后直接成功是因为它在业务上更贴近真实场景——比如四六级、计算机等级考试这类报名都会先经过院系确认。在毕设里加一个审核角色也方便你在答辩时展示状态流转比单纯的“提交即成功”更有内容可讲。1.2 毕业设计选题的本质在“能实现”和“有亮点”之间找平衡老话说得好毕设评审最看重的不是功能多而是完整性和逻辑自洽。这题目最友好的地方在于它的功能边界非常清晰考试信息展示、报名、报名审核、个人报名记录查询再加一个后台管理端足够撑起一篇论文的核心章节。那亮点从哪来三个方向就够了。第一微信生态特性这是原生技术栈的天然亮点。登录用微信开放能力实现免注册体验审核结果通过订阅消息主动通知考生小程序端的下拉刷新和加载更多也属于体验上的加分项。第二防重复报名的数据一致性处理。一张报名表加唯一索引后台再配合事务判断简单但答辩一定能讲到。第三考生端的“我的报名”页面把报名状态可视化甚至可以根据报名时间/考试时间做小统计图表Open-source 的图表库直接接入视觉效果好上手也快。守住这三样已经比大部分“增删改查管理系统”高出一截足够应付答辩。2. 技术选型原生小程序还是 uniapp后端我为什么选了 Spring Boot技术选型这块是我在项目开始前纠结最久的问题。当时身边好几个同学劝我直接用 uniapp说以后能跨 App看起来更高级。但我最后选了原生微信小程序 Spring Boot理由下面细说。2.1 小程序前端的方案对比我先把两种方案的优缺点整理成一个表格方便你直接对照自己的情况选方案学习成本调试体验体积/审核风险适合场景原生微信小程序WXMLWXSSJS低官方文档多示例直接微信开发者工具一步到位断点和真机调试顺畅主包控制在2MB以内基本无压力毕设、中小型业务只做微信端uniappVue语法中还要额外掌握HBuilderX或CLI流程多端调试但问题定位经常要绕一层打包后容易触发体积上限需分包和压缩需要同时发布App/H5/多端小程序的场景我用过一段时间 uniapp印象最深的就是热搜里那个报错“source size 2612kb exceed max limit 2mb”。项目还没写多少一张带背景图的启动页就能把主包撑爆接下来只能研究分包、图片压缩、去掉无用组件。原生小程序在这方面的门槛低得多页面多的时候做个 tabBar 分包就够了。对毕设这种规模来说控制力比跨端能力重要。原生小程序能让你在答辩现场用微信开发者工具现场展示、当场改个样式刷新这种“所见即所得”的从容感是 uniapp 给不了的。2.2 后端选型与配套后端我选了 Spring Boot MyBatis-Plus MySQL理由很直接生态成熟到不能再成熟遇到任何 Bug 都能从网上找到解决方案而毕业设计最怕的就是卡在一个冷门框架上两三天出不来。Spring Boot 的自动配置让 RESTful API 的开发非常顺手MyBatis-Plus 把单表 CRUD 几乎省到了零代码但业务稍微复杂一点也完全兜得住。配套上如果你不想引入额外的中间件可以完全依赖 MySQL 完成全部功能。但如果你想让论文里多一些含金量我建议加入 Redis用它来缓存考试列表的首页数据同时把它作为自定义 token 的存储容器。这个量级不会给自己挖坑答辩时讲起来又很有得聊。提示如果你已经会用 Node.js Express用它做后端没有任何问题。核心逻辑不在语言而在接口设计是否规范、事务和异常处理是否严谨。选熟不选生是关键原则。3. 小程序端核心页面实现踩过的坑一次说清楚小程序端的页面开发是大多数同学真正体验“理想很丰满现实很骨感”的地方。热搜里那些关键词比如“页面列表加载更多”“顶部导航栏高度”“单选框”我一个没漏全踩了一遍。这节直接把代码和坑位一起给你。3.1 考试列表的分页加载与下拉刷新考试列表是这个系统最核心的入口要求既要流畅又要完整。我最开始图省事直接一次性加载全部结果考试数据一多页面直接卡顿而且真机上快速滑动明显掉帧。后来规规矩矩做了分页。分页逻辑三件套page 页码、pageSize 每页条数、onReachBottom 触底加载。核心代码大概是这样的Page({ data: { examList: [], page: 1, pageSize: 10, hasMore: true, loading: false }, // 页面触底时自动加载下一页 onReachBottom() { if (this.data.hasMore !this.data.loading) { this.setData({ page: this.data.page 1 }); this.loadExamList(); } }, async loadExamList() { const { page, pageSize, examList } this.data; this.setData({ loading: true }); const res await wx.request({ url: https://api.example.com/exam/list, data: { page, pageSize } }); const list res.data.list; this.setData({ // 追加下一页的数据而不是覆盖 examList: examList.concat(list), loading: false, hasMore: list.length pageSize }); }, // 下拉刷新重置为第一页 async onPullDownRefresh() { this.setData({ page: 1, examList: [], hasMore: true }); await this.loadExamList(); wx.stopPullDownRefresh(); } });这里有两个细节必须写进经验里。一是二维码加载“加载更多”的体验如果后端返回的数据不足一页要把 hasMore 置为 false否则会反复请求最后一页。二是 loading 状态必须用上避免触底事件连续触发多次导致同一页的数据被重复追加。3.2 radio-group 单选框的“字符串陷阱”热搜里出现“微信小程序单选框”我猜很多人是卡在取值这一步。小程序里radio-group的 value 属性看起来是数字但bindchange事件回调里e.detail.value拿到的永远是字符串。这个坑特别隐蔽因为开发者工具里显示的时候它看起来很正常一旦你拿去做数字比较就出问题了。我当时做的是报名表单里的“学历层次”选择radio-group bindchangeonDegreeChange label radio value1 checked{{form.degree 1}} / 本科 /label label radio value2 checked{{form.degree 2}} / 硕士研究生 /label /radio-group然后在事件处理函数里onDegreeChange(e) { // e.detail.value 是字符串 1 const val e.detail.value; if (val 1) { // 这里才能正确进入分支 } }我第一次用数字比较结果条件始终不成立白白排查了一个晚上。唯一的建议所有从 radio-group 和 picker 拿到的值都先转成 Number 再往后端传后端也要做好字符串与数字兼容。这个细节在小程序开发里至少要踩一次早踩早记住。3.3 自定义顶部导航栏胶囊按钮与状态栏高度如果你用了默认导航栏标题会直接显示在小程序框架自己的顶部这没问题。但如果你想做一个更有设计感的首页——比如考试轮播图贴着顶部或者想自定义标题栏的配色——就必须走上“自定义导航栏”这条路。自定义导航栏最麻烦的是适配不同机型。不同手机的刘海屏高度不一样状态栏高度也不一样胶囊按钮的尺寸是固定的但位置会随机型浮动。我最终的适配方案是这样算的const systemInfo wx.getSystemInfoSync(); const menuButton wx.getMenuButtonBoundingClientRect(); Page({ data: { statusBarHeight: systemInfo.statusBarHeight, // 状态栏高度 menuRight: systemInfo.screenWidth - menuButton.right, // 胶囊按钮右边距 menuTop: menuButton.top, // 胶囊按钮顶部位置 navHeight: menuButton.height (menuButton.top - systemInfo.statusBarHeight) * 2 } });拿到 statusBarHeight 后自定义导航栏的总高度用胶囊按钮和状态栏做相对布局。核心思路是导航栏高度 状态栏高度 胶囊按钮与状态栏的间距 × 2。这样做出来的导航栏在 iPhone 刘海屏和安卓全面屏上都不会被物理占位挡住。这块代码放在onLaunch里通过全局变量存一份所有页面直接读取不用每个页面重复计算。一次调试全端受益。3.4 报名表单校验与体验细节报名表单是小程序端最重要的信息采集入口。考号、身份证、手机号这些字段前后端都要校验前端校验负责体验后端校验才是底线。投资人最怕前端已经提交成功后端却因为数据校验失败返回 400结果用户以为自己报名了其实没有。我建议至少做以下校验手机号用正则做 11 位校验身份证号做 18 位长度 最后一位校验位检查提交时设置一个submitting状态防止连点导致重复提交报名信息提交成功后清空表单并给出明确弹窗然后跳转到“我的报名”页面从体验上讲“我的报名”页要清晰地列出每条记录的考试名称、报名时间、当前状态待审核/已通过/已驳回。驳回的情况下最好能显示驳回原因不然用户不知道自己是材料问题还是条件不满足只能干瞪眼。4. 后端与数据库设计把报名系统的地基打稳前端页面再好看后端和数据表要是设计得稀烂答辩时被老师追问几下就露馅了。我在这个项目里把大部分时间都花在数据库和接口设计上结果证明这个时间花得特别值。4.1 四张核心表的关系与字段细节考试信息报名系统最少需要四张表用户表、考试信息表、报名表、公告表。核心关系很简单用户对考试发起报名产生一条报名记录一条考试信息可以被多条报名记录引用。用户表字段id、openid微信唯一标识、nickname昵称、phone手机号、avatar头像、degree学历、create_time。考试信息表字段id、exam_name考试名称、exam_type考试类型如等级考试/资格考试、location考试地点、start_time报名开始时间、end_time报名截止时间、quota名额上限、status报名中/已截止/已满、description考试说明、create_time。报名表字段id、user_id用户ID、exam_id考试ID、status待审核/已通过/已驳回、remark驳回原因或审核备注、create_time、update_time。公告表字段id、title、content、create_time。这里的边界要守住考试信息表和报名表是多对多关系还是需要专门设计关联表答案是——在这个系统里你只需要让“一条报名记录对应一场考试”即可这就是多对一关系。报名表里的exam_id作为外键指向考试表配合user_id就能把所有关联查询跑通。如果你非要引入独立的“用户-考试关联表”反而把一个简单业务复杂化了。四张表里最重要的设计是报名表的唯一索引ALTER TABLE registration ADD UNIQUE KEY uk_user_exam (user_id, exam_id);面试官很喜欢问的一个问题是如果用户重复点击报名按钮你怎么保证不会产生两条报名记录答案就在于这个唯一索引。它在数据库层就拦截了重复报名比后端的 if 判断更可靠。4.2 接口设计报名系统的六个核心接口后端接口我按功能域拆成两组考生端接口和管理员端接口共六个核心接口。学生端GET /api/exam/list— 考试信息分页列表支持按类型筛选GET /api/exam/detail?idxxx— 考试详情POST /api/registration/apply— 在线报名GET /api/registration/myList— 查询我的报名记录管理端POST /api/admin/exam/publish— 发布考试POST /api/admin/registration/review— 审核报名每个接口要有统一的响应体我习惯用这样的格式{ code: 0, message: success, data: {} }code 为 0 表示成功非 0 表示各类业务异常。前端根据 code 做统一的提示处理不需要每个接口都写一套 try catch。报名这个接口需要注意并发问题。考试名额只有 100 个结果 150 个人同时报名怎么办最基础的处理方案是在后端加事务 行锁Transactional public void apply(ApplyRequest req) { // 1. 根据考试ID锁定考试记录 Exam exam examMapper.selectByIdForUpdate(req.getExamId()); // 2. 检查报名时间考试是否还在报名期内 if (exam.getStatus() ! 1) { throw new BusinessException(该考试当前不在报名时间内); } // 3. 统计已报名人数 int count registrationMapper.countByExamId(req.getExamId()); if (count exam.getQuota()) { throw new BusinessException(该考试名额已满); } // 4. 插入报名记录 registrationMapper.insert(...); }selectByIdForUpdate对考试记录加悲观锁同一时刻只有一个请求能通过检查其他请求会等待锁释放。这里不需要 Redis 分布式锁那是高并发业务比如秒杀才需要考虑的方案在毕设这个量级下数据库锁已经足够严谨而且是面试官认可的思路。5. 微信生态的独有细节登录态、订阅消息和上线审核如果说前面的内容任何考试报名系统都通用那这一节才是微信小程序项目区别于传统 Web 项目的关键。你在答辩时能发挥出来的差异化亮点基本都集中在这里。5.1 微信登录wx.login 与自建登录态小程序登录最大的好处是“无感”用户不用输账号密码微信已经帮我们解决了身份认证的问题。以前的老方案是用wx.getUserInfo直接拿手机号后来微信官方收紧了接口权限改成需要单独授权。我现在用的标准流程是// 小程序端 wx.login({ success: async (res) { const loginRes await wx.request({ url: https://api.example.com/api/login, method: POST, data: { code: res.code } }); // 后端返回自定义token存储到storage wx.setStorageSync(token, loginRes.data.data.token); } });后端的登录接口拿到 code 后用它向微信服务器换 openid 和 session_key然后生成自己的 token 返回给前端。这个 token 我建议用 UUID 或自定义字符串存入 Redis 并设置过期时间就行不需要单独引入 JWT。为什么建议做成自建 token因为毕设的规模不需要引入 JWT 依赖而且自建 token 配合 Redis 可以随时控制用户会话过期处理退出登录也更方便。答辩时你就可以顺口讲出来用户身份其实是微信帮你验证的后端只信任微信返回的 openid。注意千万不要在代码里直接把 openid 返回给小程序端保存。openid 是用户的身份标识一旦泄露且没有额外安全措施接口层的鉴权就形同虚设。正确做法是后端存 openid发一个随机 token 给小程序端。5.2 订阅消息报名审核结果通知这个功能我给满分推荐它是整个项目里最贴近真实业务需求、答辩时最容易被记住的亮点。核心流程是用户提交报名后弹窗引导授权订阅消息管理员审核通过或驳回时后端通过微信订阅消息 API 给用户推送一条状态通知。用户在前端做的动作很简单wx.requestSubscribeMessage({ tmplIds: [你的模板ID], success(res) { // 用户点击了“允许” if (res[你的模板ID] accept) { // 记录用户已授权后端保存token } } });后端发送通知时需要将这个模板的发送权限先在小程序管理后台申请好拿到 template ID并把用户的 openid 和订阅凭证存下来。订阅消息有一次性特性用户点一次“允许”只能收到一条消息所以如果审核被驳回后用户再次报名后台要重新弹一次授权。这个逻辑别漏不然上报后你会发现消息永远推不到。这块代码不复杂但需要你耐心看完微信官方文档的“订阅消息”章节把小程序端、后端、微信后台三处的配置串起来。5.3 开发调试与打包上线的几个坑开发阶段真机和工具的表现可能完全不一样。我在测试“考试详情页分享给班级群”时自己手机模拟没问题真机老是一跳一闪。后来发现是域名没有配置到微信后台的合法域名列表里。你可以先到微信公众平台把你的 API 域名加到 request 合法域名中开发工具里也勾上“不校验合法域名”保证调试顺畅。但要记住上线前必须把“不校验”取消不然会被审核方直接打回。调试接口时微信公众号是 HTTPS 域名本地开发一般用 Charles 看看请求和响应内容。用它抓包能看到小程序发了哪些参数、后端返回的 JSON 长什么样排查联调问题很方便。抓包工具本身只是为了开发调试不涉及任何别的用途。再补一个热搜里的高频问题微信开发者工具里的小程序怎么发给别人试用。点击工具上方的“预览”按钮会生成一个带二维码的预览页面其他人用微信扫码就能在真实手机上打开你的小程序不需要托管代码。这个功能我强烈建议你在答辩前用起来让同学扫码收集几天真实反馈能发现不少你一个人测试时发现不了的问题比如按钮间距、文字溢出、不同手机的兼容性。最后提交审核之前请把主页的“考试信息”默认数据填满不要留空白状态。审核员进入小程序的第一印象决定了你的审核过不过。6. 答辩演示的实战经验三分钟抓住评委注意力项目做完只是第一步答辩演示决定了你最终分数的上限。这里我分享几个自己实践过的经验对考试报名系统这个题目特别有用。6.1 演示前必须准备的演示数据很多人的演示翻车不是代码坏了而是数据准备不足。我亲眼见过同学现场报名结果考试列表里只有一条记录点进去就报名成功了评委还没来得及看清流程演示已经结束。建议提前准备三个状态的考试数据考试报名中有充足名额、考试已满名额用尽、考试已截止过了报名时间。演示时先展示一个正常报名流程再切换到“已满”状态然后演示后端审核驳回再回到小程序查看状态——这样整个状态流转就很饱满评委能看明白业务闭环。另外把“我的报名”页也准备好路径现场演示从“报名成功”到“待审核”再到“已通过”的完整时间线。这个页面数据要提前造好最少包含三条不同状态的记录。6.2 答辩常见问题与回答思路面试官/评委问的最多的几个问题基本都绕不开这四类提前准备好话术现场就会很稳。“你的需求是怎么来的” —— 答“我访谈了几位考务管理老师发现线下报名存在信息不透明、手工统计易错、通知不及时的问题所以围绕这三个痛点设计了这个系统。” 三个痛点对应系统的三个功能模块逻辑闭环。“跟网上现有的开源报名系统相比你的亮点在哪” —— 答“一是微信生态免注册登录二是订阅消息主动通知三是通过数据库唯一索引与事务保证报名数据一致性。”“数据一致性是怎么保证的” —— 打开你的数据库设计文档把唯一索引和排他锁的 SQL 亮出来然后讲清楚加锁的成本和适用场景。“并发量大了怎么办” —— 不要慌这是一个测试深度的扩展题。你先说当前的悲观锁方案保证了正确性如果未来报名人数大幅上涨可以引入 Redis 预扣库存方案或消息队列异步化。这里不用展开代码点到为止。我记得答辩时评委最在意的不是你的代码多花哨而是“你有没有真正想清楚每一步为什么这么做”。当你把“为什么选原生小程序”“为什么加唯一索引”“为什么用订阅消息”都讲清楚时分数的下限已经很高了。最后再分享一个实用技巧演示前把手机调成勿扰模式蓝牙和 Wi-Fi 保持开启如果需要投屏提前在答辩教室里测试一次微信开发者工具的投屏效果避免现场临时手忙脚乱。把演示当成“讲清楚一个解决实际问题的完整故事”来做你就已经超过现场一大半的人了。