毕业设计拿到“基于微信小程序的游泳管理系统”这个题目时,我的第一反应是:这不就是一个预约软件吗?等真正把业务拆开,发现游泳馆要管的远不止预约——办卡、约教练、签到、查课表、统计到场人数,每一块都得想清楚。这篇记录我从零搭完这个项目的完整思路和踩坑过程。配套的 LW 文档怎么组织、源码目录怎么规划、哪些代码一看就是加分项,我也都会讲到。如果你正在选毕设方向,或者接了游泳场馆管理的小需求,这篇应该对你有直接帮助。
这个题目的受众其实很明确:计算机专业准备毕业设计的学生,或者想做小程序练手项目但不想做那种烂大街商城的人。游泳管理系统的好处是业务场景足够实体化——人有角色、场有状态、钱有订单、课有排期,改造成其他场馆系统(健身房、羽毛球馆、自习室)非常容易。
1. 从选题到技术栈:微信小程序和游泳管理系统的组合逻辑
1.1 为什么游泳馆需要一套“管理系统”
游泳馆的实际运营场景比你想象的复杂。我在调研阶段跑了两家当地游泳馆,发现前台最头疼的几个问题:电话预约经常冲突,弹性时间段没法精确管理;会员卡办了但忘带,全靠报手机号查询,效率低还容易出错;私教课排课靠Excel,教练变更课时学员根本不知道。
所以游泳管理系统的核心价值不是“把纸质表变成电子表”,而是解决三件事:场地时段的可占用状态实时可见、预约流程的闭环(预约→到场→签到→核销)、用户的身份识别(谁来过、买了什么、剩几次)。把这三个问题想清楚,后面的表设计和接口设计就有了依据。
1.2 小程序不是唯一选择,但确实是最省事的选择
做毕设时你可能纠结过:为什么选微信小程序而不做个网页端或者App?
我的观点是,小程序在这类场馆场景里有天然的生态优势:用户不用下载安装,扫码就能用;微信自带登录体系,省去自己搞账号系统的麻烦;如果需要推送“预约成功”“教练调课”通知,订阅消息也是现成的。对比一下成本,App需要适配安卓和iOS、还要考虑上架审核;网页端做完还得解决“用户怎么找到入口”的问题。小程序是最贴近“用完即走”的工具型产品形态,很匹配游泳馆这种低频但刚需的预约场景。
当然小程序也有自己的坑:包体大小限制、审核规则多、登录换手机号接口个人主体受限。这些我放到后面单独讲,都是实际会被卡住的地方。
1.3 技术栈选择:原生还是uniapp,后端怎么搭
毕设项目的技术栈选择,我建议遵循“稳定优先、能答辩说清原理”的原则。
前端方案对比:
| 方案 | 优点 | 缺点 | 建议 |
|---|---|---|---|
| 微信原生小程序 | 官方文档全、调试方便、编译产物最小 | 只有 wxml/wxss,代码复用不如 Vue | 没人带又想稳扎稳打,选它 |
| uniapp | Vue 语法、一套代码多端发布 | 二次封装的问题定位麻烦,打包后可能超 2MB | 想顺带学跨端开发,可选 |
后端我推荐 Spring Boot,因为计算机毕业设计的主流选题还是 Java 方向,答辩时老师对 Spring Boot 的提问范围是可以预判的(依赖注入、自动配置、拦截器这些)。如果你不熟 Java,用 Node.js(Express/Koa)或者微信云开发也能做,但要注意答辩时老师可能会盯着你问“为什么不用主流方案”。
数据库基本就是 MySQL,老牌稳定,表关系也好讲清楚。Redis 不是必须的,毕设这种并发量用数据库事务就够,加了 Redis 反而要多解释一堆缓存一致性的问题。
提示:如果你选 Spring Boot,端口、接口前缀、跨域配置、统一返回结果这些基础工程一定要提前搭好。后面每个模块只是往里填业务代码,不然边写业务边补工程问题,节奏会乱。
2. 系统设计与数据建模:把游泳馆的业务拆成表
2.1 角色和业务流程:这步不能懒
建模前一定要先走一遍业务闭环,我见过很多同学上来就画 ER 图,结果连“用户预约后怎么签到”这个流程都没想清楚。游泳管理系统的核心流程是:
用户注册登录 → 查看场地/课程/教练 → 选择时间预约 → 提交订单 → 模拟支付 → 到馆签到 → 管理员核销/查看统计
涉及的角色有普通用户(泳客)、教练、管理员。注意,教练和管理员都应该在小程序里完成操作,不要做两个端。最简单的方式是用户表加一个 role 字段,前端按角色显示不同页面,后端在接口层做权限校验。这样省掉一套后台管理系统的开发量,但功能看起来依然完整。
2.2 核心数据表:从 users 到 reservations
我在设计表时踩过一个坑:想着把所有字段都塞进一张大表里,后来发现“预约记录”和“订单”其实是两类东西。下面是我最终定稿的核心表清单:
| 表名 | 作用 | 关键字段 |
|---|---|---|
| users | 用户基础信息 | openid、nickname、phone、role、status |
| coaches | 教练信息 | name、title、phone、intro、avatar |
| courses | 游泳课程 | course_name、course_type、coach_id、price、max_count |
| periods | 场馆时段 | start_time、end_time、price、capacity |
| reservations | 预约记录 | user_id、period_id、course_id、reserve_date、status |
| orders | 订单支付 | order_no、user_id、reservation_id、amount、pay_status |
| cards | 会员卡 | user_id、card_type、total_count、used_count、expire_date |
其中 reservations 表是整个系统的核心,它决定了“场地时间段”到底有没有被占掉。我的设计是:场馆按小时间段(比如每 30 分钟一段)拆成 periods 表,预约记录里存 reserve_date + period_id,这样一天多时段、多场地都可以扩展。如果你需要更细粒度(达到“同一个泳道”的预约),再加一个 lane_id 字段即可,预留扩展位很重要。
2.3 容易写错的字段设计:状态、时间、软删除
第一个坑是状态字段。预约状态至少要预留:0待签到、1已签到、2已取消、3已过期。很多同学只写“已预约”和“已完成”两个状态,等到做签到功能时发现还要区分“能不能签”“超时了怎么办”,改表结构很痛苦。
第二个坑是时间字段的类型。reserve_date 用 DATE 类型,start_time/end_time 用 TIME 类型,不要在 Java 里用字符串拼日期去比较,MySQL 的日期函数在统计时好用得多。
第三个坑是软删除。用户可能注销、管理员可能删错数据,每条核心表都加一个 delete_flag 字段(0正常 1删除),所有查询默认带上WHERE delete_flag = 0。毕设答辩时,这个设计能直接回答老师“你如何处理数据安全性”的问题。
另外提醒一点:微信登录拿到的 openid 一定要做唯一索引。同一个用户每次登录返回的 openid 是固定的,用它做登录凭证能天然避免重复注册问题。
3. 核心功能实现拆解:预约、签到、购票的落地细节
3.1 小程序端页面架构与自定义导航栏适配
页面规划上我分了五块:首页、场地预约、课程预约、我的预约、个人中心。底部 tabBar 放四个入口,个人中心放“管理员入口”按钮(仅 role 为管理员时显示)。
这里有一个热搜词提到的问题:微信小程序顶部导航栏高度。不同机型的胶囊按钮位置不一样,标题栏高度也变,如果使用自定义导航栏(navigationStyle: custom),页面顶部的文字和按钮很容易错位。我当时的适配代码是:
// 获取胶囊按钮信息 const menuButton = wx.getMenuButtonBoundingClientRect(); // 获取状态栏高度 const windowInfo = wx.getWindowInfo(); const statusBarHeight = windowInfo.statusBarHeight; // 导航栏高度 = (胶囊顶部 - 状态栏高度) * 2 + 胶囊高度 const navBarHeight = (menuButton.top - statusBarHeight) * 2 + menuButton.height;这段代码建议封装成公共模块,所有自定义导航页面调用,避免每个页面复制粘贴。真机测试时多找几台不同型号手机看看,总有一款会给你惊喜(负向的那种)。
3.2 场馆时段预约:怎么防止两个人抢同一个时间段
这是整个系统最核心的业务逻辑。用户选择日期和时段,点了预约之后,后端要保证“同一场地的同一时段不能被重复预约”。
最直接的方案是数据库唯一索引 + 插入时捕获冲突。我在 reservations 表里给(venue_location, reserve_date, period_id)建立了唯一索引,然后预约接口的核心逻辑是:
@Override @Transactional(rollbackFor = Exception.class) public Result createReservation(ReservationDTO dto) { // 检查用户是否已有冲突预约 // 检查场地时段是否已被占用 // 插入预约记录 // 生成配套订单 }这里的关键是@Transactional事务注解,以及插入唯一索引冲突时捕获DuplicateKeyException。前两步的“查询再判断”在高并发下其实有竞态,但唯一索引兜底,保证极端情况下数据库也不会插入两条冲突记录。毕设答辩能被问到的并发问题,这一个点就够讲清楚你的设计思路了。
我当时也考虑过SELECT ... FOR UPDATE悲观锁,但毕设场景演示不出差别,反而会让代码更复杂。如果你做的是课设,用“唯一索引+事务”已经超过多数人的水平了。
3.3 课程预约与名额控制:满员之后怎么办
课程预约的冲突检测方式和场馆预约略有不同:课程不按固定时段重复,但有一个“最多报名人数”的限制。核心逻辑如下:
// 课程预约时,统计当前报名人数 Integer currentCount = reservationMapper.countByCourseId(courseId); Course course = courseMapper.selectById(courseId); if (currentCount >= course.getMaxCount()) { return Result.error("该课程已满员"); }同样的逻辑,建议在插入前判断,插入后用事务保持一致性。这里的用户体验细节是:前端在课程卡片上也要实时显示“已报 x/max 人”,需要后端提供一个查询接口返回名额余量。不要只做一个满员时点击报错,应该提前让用户看到名额情况。
3.4 签到功能:一次性完成但有三种实现姿势
签到实现我建议做成“用户端自签 + 管理员端代签”的双通道。
用户端逻辑:在我的预约列表中找到当天的预约记录,点击“签到”按钮,后端校验当前时间与预约时段的关系。我允许提前 15 分钟、延后 15 分钟内签到,超出这个窗口提示“不在签到时间”。代码里要特别注意时区问题,直接用new Date()比较前端传来的时间戳,不要在数据库里做字符串比较,否则夏令时和其他时区问题会埋雷。
管理员端逻辑:管理员可以查看当日全部预约列表,手动将某人标记为已签到。这样做的好处是覆盖了“用户忘带手机、手机没电、老人小孩不会操作”的情况。你还可以加一个扫码签到的增强——生成预约二维码,管理员小程序扫码后自动核销。二维码我用的是wx.createQRCode(适用于小程序码),或者用普通二维码库生成一张预约编号图片,流程都行。但扫码需要摄像头权限,小程序隐私接口要配置好。
3.5 订单与模拟支付:不接真实微信支付
真实微信支付需要商户号、企业资质认证,个人主体毕设基本走不通。我当时直接做了一个“模拟支付”按钮:用户点击支付后,进入一个支付确认页,点击“确认支付”后,后端把 orders 表的 pay_status 从 0 改成 1,并把 reservation 状态从 0 待支付改成 0 待签到。这里要注意,预约记录和订单状态是一对一关联的,支付成功前预约状态可以叫“待确认”或“锁定”,不要和“待签到”混淆。
如果你想加上一点真实感,可以接入微信支付的原生流程展示,但实际提交订单时走模拟链路。老师在验收时如果问“为什么不做真支付”,你答“需要企业商户号和资质,个人主体受限,使用模拟支付保证流程闭环”,这个回答在毕设场景里是加分的,说明你懂边界。
注意:即使模拟支付,也要在订单表设计好支付时间、支付方式字段。真实系统里这些字段是财务对账的关键,我先预留好,后续接入真实支付不用改表。
4. 微信登录与手机号授权:这个环节最容易把人卡死
4.1 wx.login 拿到 code 之后的完整链路
微信登录的完整链路是:小程序端调用wx.login()拿到临时 code → 传给后端 → 后端拿 code 调微信接口换取 openid 和 session_key → 后端生成自己的登录态 token 返回前端 → 后续所有请求带 token。
这里最容易被忽略的是:后端不能直接把 openid 当作用户的“会话凭证”用,因为它没有过期时间概念,被拿到就能冒充用户。正确做法是:后端自己生成 token(随机UUID)存到 Redis 或数据库,同时设一个过期时间(比如 7 天),以后每个需要登录的接口通过拦截器校验 token。
换 openid 的典型代码我贴一下:
// 小程序登录 code 换会话信息 String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appid + "&secret=" + secret + "&js_code=" + code + "&grant_type=authorization_code"; // 返回 json 中包含 openid、session_key需要注意:appid 和 secret 一定不要写在前端代码里,也不要提交到 GitHub。我见过不少同学把这些配置硬编码在项目里传到开源仓库,结果被别人盗刷接口。正确做法是放到application.yml里并加入.gitignore,提交代码前检查一下。
4.2 getPhoneNumber 的认证门槛和替代方案
现在微信小程序获取用户手机号有两种方式:一种是通过button open-type="getPhoneNumber"点击后拿到动态令牌 code,再调用后端接口换手机号;另一种是原来的填写表单方式。但是这个接口现在有严格限制:非企业主体小程序无法调用,个人主体申请不到这个接口权限,而且还需要在小程序管理后台配置用户隐私保护指引。
我当时是一开始照着网上的文章调 getPhoneNumber,点了按钮之后一直是 undefined,查了二十多分钟才发现权限问题。如果你也做个人主体的毕设,我建议直接用替代方案:注册时让用户手填手机号 + 短信验证码(后端模拟发送验证码,或者用阿里云短信但需要企业资质),登录时直接用微信一键登录。核心思路是:能用微信身份就微信身份登录,需要手机号用于业务联系时,在个人资料里补填。
另外还有一个坑:模拟器的网络环境可能和真机不一致,getPhoneNumber 在开发者工具里能弹出来,真机上就报错。调试时要多留意这个差异,别等答辩前一天才发现。
4.3 session_key 的保密注意
session_key 是调用微信敏感接口时用来解密的密钥,绝对不能返回给前端,更不能打进日志里。如果换手机号接口需要解密数据,session_key 的服务端获取逻辑也只在后端做。很多毕设项目把 session_key 直接通过接口返回,这是严重的安全漏洞,答辩老师发现了会直接扣分。
5. 打包发布与真机调试:把 Demo 变成能演示的系统
5.1 本地开发环境的 HTTPS 和域名校验
小程序请求后端接口有一个硬规定:正式版小程序只能请求https://且域名已在小程序管理后台配置过的接口。本地开发时,开发者工具可以勾选“不校验合法域名”,但我建议从第一天起就使用统一的baseUrl常量管理接口地址,方便后面切换。
真机调试时,如果手机上打开了调试模式,可以跳过域名校验,但每次发布体验版/正式版时必须校验。还有一个细节:开发阶段的 IP 地址(比如http://192.168.1.100:8080)只能在同一个局域网的真机上访问,云服务器部署时还要考虑跨域问题。
5.2 包体积超限:处理 uniapp 的 2MB 魔咒
热搜里有一条:“uniapp 微信小程序打包 source size 2612kb exceed max limit 2mb”。这个坑我亲眼见过不止一个同学踩过。小程序主包体积上限是 2MB(整个小程序目前是 20MB 左右,但主包是 2MB),图片、原生组件、第三方库一多就容易超。
解决办法最有效的是分包加载:
- 主包只保留 tabBar 页面、公共组件、公共工具类
- 场地预约、课程详情、预约记录这些页面全部放进分包
pages/booking/ - 分包之间页面跳转路径用
/pkg-booking/pages/...格式
图片处理是个大头:不要在项目里放大量本地图片,全部压缩后上传到服务器或图床,用https://链接引用。如果项目用到地图、图表这类大组件库,按需引入,不要整包 import。
5.3 真机调试中常见的差异
模拟器跑通了,真机却出问题,这是小程序的常规操作。我遇到过的差异有:
- 模拟器的 storage 数据和真机不互通,登录态经常“消失了”,这不是 bug,是环境隔离
- 自定义导航栏的高度适配:模拟器上完美,iPhone 14 Pro Max 上胶囊和文字挤在一起
- 权限弹窗:获取位置、摄像头、录音权限时,真机会弹出系统授权框,需要在 app.json 里声明对应
permission字段 - 上传图片:模拟器可以直接选本地文件,真机需要调用
wx.chooseMedia,返回格式有差异
所以项目做差不多时,借一台安卓一台 iPhone,把主要流程走一遍,比写一百个console.log都管用。
5.4 审核提审的注意事项:毕设演示不上线怎么办
毕设作品不一定要上线,只要你能演示就行。走开发版或体验版即可,不需要提交审核。但如果你打算上线让别人用,有几个点:
第一,微信小程序现在要求先完成备案才能发布。第二,个人主体很多类目不可用(体育场馆预约一般需要企业或个体工商户资质)。第三,涉及用户信息收集需要在后台配置《用户隐私保护指引》,并声明收集手机号的用途。第四,符合类目要求的服务范围里选“体育 > 体育场馆服务”。
毕设答辩场景里,我建议你就用“体验版”给老师演示,现场扫码即可,最稳妥,也省去审核等待时间。
6. LW文档和答辩准备:代码之外的最后一公里
6.1 配套文档结构怎么搭,和源码对照着写
跟“源码”配套的 LW 文档(论文/文档)在很多时候比代码更影响成绩,因为它直接决定了答辩老师能不能快速看懂你的项目。我的文档目录是这样的:
- 摘要 + 关键词
- 需求分析(业务痛点、角色分析、可行性分析)
- 系统设计(总体架构、功能模块划分、数据库设计)
- 系统实现(核心功能、界面展示、核心代码讲解)
- 系统测试(测试用例、结果分析)
- 总结与展望
关键技巧是:文档里的截图必须从你的实际运行系统里截,不要用网图。数据库设计章节直接把建表 SQL 贴进去,并在关键字段上加上注释。核心代码讲两块就行:登录流程的 token 鉴权、预约防冲突的设计。不要大段贴页面代码,老师更关心的是思路。
6.2 测试用例整理:10 条就够用但有讲究
系统测试部分不需要几百条用例,按功能模块整理 10-15 条高价值用例即可。例如:
| 编号 | 测试模块 | 操作步骤 | 预期结果 |
|---|---|---|---|
| T01 | 登录 | 点击微信登录 | 自动创建账号,跳转首页 |
| T02 | 场地预约 | 选择已被预约的时段 | 提示“该时段已被预约” |
| T03 | 场地预约 | 两个账号同时间约同一时段 | 只有一个成功 |
| T04 | 课程预约 | 课程已满员时点击报名 | 提示“已满员”,不能提交 |
| T05 | 签到 | 非签到时间段签到 | 提示“不在签到时间” |
| T06 | 管理员 | 管理员登录后查看统计数据 | 显示今日预约数、到场数 |
每条用例都对应一个可复现的操作路径。答辩时老师如果问“你这个系统怎么保证不出 bug”,把测试用例表调出来,直接加分。
6.3 答辩高频问题和容易漏讲的设计点
我整理了几个老师最爱问的问题以及你应该提前准备的答案:
- “为什么选择微信小程序而不是App?”——轻量、免安装、微信生态内登录与通知能力
- “系统的角色和权限是怎么设计的?”——users 表的 role 字段 + 后端拦截器按角色放行接口
- “数据库表之间怎么关联?”——重点讲 reservations 表如何关联 users、periods、courses
- “遇到的最大难点是什么?”——并发预约冲突,用唯一索引 + 事务解决
- “项目有哪些不足?”——真实线上支付未接入、未做大数据量压力测试,后续可以完善
还有一个设计点一定要准备:管理端的数据统计功能。不用做复杂图表,一个“今日预约数、今日签到数、本月课程收入”的卡片式汇总页就足够。这个功能在演示时非常亮眼,工作量却不大,我强烈建议加上。
在实际操作中,我最想提醒你的是:代码和文档一定要同步更新。我见过太多人写完代码再补文档,最后补出来的文档跟源码对不上,字段名换了、流程变了,老师对着代码问一个问题就直接穿帮。把文档跟开发周期同步推进,哪怕每一周只写一小节,最后也只是合并汇总,不会开夜车赶工。
这个小程序做完之后,改动的地方也比你想象的多。真正上线或者给真实场馆用时,要处理支付回调、教练排班冲突、短信通知、数据备份这些生产问题。但作为毕设项目,把预约闭环、角色权限、数据建模这几块讲清楚,已经是一份能拿得出手的作品了。如果后面有想法,可以把这个系统往“体育场馆多店版”改,一套后台管多个场馆,那又是另一个项目了。