1. 项目全景:美容服务小程序到底在做什么
每年毕业季,计算机专业的同学都会面临同一个灵魂拷问:毕设做什么。管理系统太老套,纯前端页面撑不起工作量,算法方向又怕翻车。如果你看到"基于微信的美容服务小程序"这个题目,千万别觉得它只是一个普通的管理系统换皮,它其实是一个把C端用户服务、B端商家管理、移动端预约支付全部串起来的完整业务闭环。
这个项目的核心价值在于:它模拟了真实生活场景里的美容院线上服务流程。用户打开微信小程序,看到店铺展示、服务项目、技师信息,在线选择合适的项目和时间段下单预约,到店消费后可以对服务进行评价;后台管理员维护服务项目、排班信息、订单数据;技师角色可以查看自己的预约日程、确认接单。从业务角度讲,它和现在市场上大量Saas美业系统做的事情本质一样,只不过以毕业设计的体量来实现,既完整又不至于失控。
适合什么人做?第一,Java或前端技术栈在校学生,这套作品源码配套齐全,能快速上手改造成自己的毕设;第二,想在小程序方向入门、对全栈开发感兴趣的自学者,通过一个真实业务系统理解前端交互、后端接口、数据库三者如何协同;第三,不太擅长从零做设计、但想做电商或O2O类选题的同学。读懂这个项目,你不仅能应付毕设答辩,还能真正搞明白一个小程序项目的完整生命周期。
我在实际审阅这套源码和使用体验后,最大的感受是:它的业务模块切得很标准,前端用的是微信原生小程序框架(WXML+WXSS+JS),没有上来就堆乱七八糟的组件库,这对学习和二开都很友好。后端接口设计也比较清晰,LW文档覆盖了从需求分析到数据库设计的全过程,拿来改改就是一个很扎实的毕业设计。
2. 技术选型:每个决定背后都有理由
2.1 小程序端:原生还是uniapp
很多同学拿到源码后第一个问题就是:这个项目为什么不用uniapp?说实话,对于毕设场景,原生微信小程序往往比跨平台框架更稳妥。uniapp的强项是一次编写、多端运行,但如果你根本没有多端发布的需求,那这一层抽象反而增加了调试成本和框架升级风险。原生小程序在开发者工具里跑起来最直接,页面结构、生命周期、事件绑定一目了然,答辩时老师问你某个组件怎么实现的,你能直接指到代码,不需要绕一层的解释。
而且微信原生框架这几年也吸收了很多Vue的写法,比如数据绑定、列表渲染、条件渲染都极其相似。你只要懂HTML和JavaScript,上手成本很低。项目里用了模块化的方式组织页面,每个页面包含 .wxml、.wxss、.js、.json 四个文件,这种结构和Vue的单文件组件比确实繁琐,但胜在每个文件职责单一,对新手理解更友好。
2.2 后端与数据库:SSM和SpringBoot的选择
后端源码通常有两种常见实现:一种是基于Spring Boot的RESTful API,另一种是SSM框架(Spring+SpringMVC+MyBatis)的传统分层结构。如果你拿到的是Spring Boot版本,我认为是好事。Spring Boot内置Tomcat,省去繁琐的XML配置,启动就是一个main方法,这对毕业答辩是巨大优势——评审老师让你现场演示,你半分钟内能起服务,观感完全不同。
用Spring Boot有一个容易被忽略的隐性优点:它默认的依赖管理帮你锁定了大量jar包版本,不会出现以前SSM时代那种jar包冲突、缺包、版本不兼容的连环坑。数据库层面用MyBatis做ORM,SQL语句可控可查,很适合业务相对常规、查询条件比较固定的管理系统型项目。MySQL配MyBatis,也是目前企业里中小型项目最常见的技术组合,写在简历上不会有违和感。
2.3 从源码角度读懂LW文档的作用
LW文档,简单理解就是毕业设计的文字成果,通常是开题报告、毕业论文、任务书这一整套。源码解决的是"系统能不能跑起来",LW文档解决的是"你是一个有工程思维的学生"。这两者在答辩里缺一不可。
比较规范的LW文档结构一般包含:项目背景与意义、国内外研究现状、需求分析(功能需求、非功能需求)、系统设计(总体架构、功能模块设计、数据库设计)、系统实现(核心技术、关键代码说明)、系统测试(测试用例和结果)、总结与展望。说白了,这就是一个完整的软件工程过程记录。写论文时不要照抄别人的,要按你的实际代码去改数据库表和接口说明,老师随机问一个表结构的时候你才能对答如流。
2.4 数据库表设计的高频细节
不管是已经存在的源码还是你要二次开发,下面几类表是这个美容服务项目一定会涉及的:用户表(会员信息)、技师表、服务项目表(也可以和美容项目表合并)、预约订单表、评价表、管理员表。我拿预约订单表举个例子:
CREATE TABLE `t_order` ( `id` int(11) NOT NULL AUTO_INCREMENT, `order_no` varchar(32) DEFAULT NULL COMMENT '订单编号', `user_id` int(11) DEFAULT NULL COMMENT '用户ID', `technician_id` int(11) DEFAULT NULL COMMENT '技师ID', `service_id` int(11) DEFAULT NULL COMMENT '服务项目ID', `appointment_date` varchar(20) DEFAULT NULL COMMENT '预约日期', `appointment_time` varchar(20) DEFAULT NULL COMMENT '预约时段', `status` tinyint(1) DEFAULT NULL COMMENT '订单状态:0待支付 1已支付 2已完成 3已取消 4已退款', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `pay_time` datetime DEFAULT NULL COMMENT '支付时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;一个容易被忽视又特别重要的细节是编码格式,我强烈建议建库建表时统一用utf8mb4,而不是utf8。因为utf8mb4才能存下 Emoji 表情和一些特殊的生僻字,在小程序端用户昵称、备注字段非常容易出现这种场景。如果建表时选错编码,后续你光改数据库配置就能耗掉半天时间。
3. 核心功能模块的代码级实现
3.1 首页装修与数据渲染
登录小程序后,第一个页面就是首页。这个页面通常包括轮播图、服务分类入口、推荐服务列表三个区域。前端通过wx.request调用后端接口,拿到 JSON 数据后绑定到data里,用wx:for循环渲染出来。
我建议你在改代码时重点研究两个地方:一是封装统一的request工具函数,把baseUrl、token自动注入和异常错误处理都集中在一个文件里,这样页面里的请求代码会瘦身很多;二是下拉刷新和触底加载分页的实现,不要一次性把几百条数据全返回,这样体验很差,标准做法是后端接口用limit和offset参数,前端触底时把当前页码加一再请求一次,直到返回的数据条数小于每页数量为止。
首页的服务项目列表接口大致是这种风格:
@GetMapping("/service/list") public Result list(@RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "10") Integer limit, String categoryId) { PageResult pageResult = serviceItemService.queryPage(page, limit, categoryId); return Result.ok(pageResult); }前端在onReachBottom里写分页加载逻辑,同时用一个isLoading标志位防止重复请求。这些设计虽然朴素,但正是企业里非常通用的分页加载模式。
3.2 预约下单:从选服务到选技师
预约模块是整个项目最核心的业务,也是逻辑最复杂的一部分。用户在服务详情页点击"立即预约",进入确认订单页,选择到店时间、选择技师(或者由系统自动分配),然后提交订单生成待支付记录。
这里的关键点在于"服务时长"和"可预约时段"的联动。一个美容项目可能耗时60分钟或者90分钟,如果不同时长混在一起,技师排期就会混乱。比较省事的做法是把一天的营业时间切成固定时间段,比如每个小时一个时段,或者半小时一个时段,下单时用户选的是"时段"而不是精确到秒的时间点。系统在后端校验该技师在所选时段内是否已有其他订单,如果有就提示冲突。
校验逻辑可以这样写:
// 检查该技师的某个时间段是否已经被预约 Integer count = orderMapper.checkTechnicianConflict(technicianId, appointmentDate, appointmentTime); if (count != null && count > 0) { return Result.error("该技师在此时间段已有预约,请选择其他时间"); }对应SQL里的核心条件就是:
SELECT COUNT(*) FROM t_order WHERE technician_id = #{technicianId} AND appointment_date = #{appointmentDate} AND appointment_time = #{appointmentTime} AND status IN (0, 1)注意这里只统计待支付和已支付状态的订单,因为已取消和已完成的订单不应该占用档期。这种细节搞清楚后,你在答辩时被问"怎么避免订单冲突"就可以回答得非常透彻。
3.3 订单状态机与支付逻辑
支付是电商类项目的点睛之笔,但毕业设计里真实接入微信支付需要企业主体、商户号、证书密钥等一堆资质,学生个人很难快速搞定。所以源码中通常有两种做法:一种是支付接口走微信支付的预下单模式,异步回调改订单状态,但只实现到预留的接口层次;另一种更常见的是用一个"模拟支付"模块,用户点击"立即支付"后直接跳到一个提示页,后端把订单状态改为已支付。
如果你不想在答辩演示时卡在支付环节,我建议先保留模拟支付逻辑,但论文里要写清楚:如果要上线商用,只需替换WxPayService这个接口的实现类,接入微信官方API即可。这样既展示了你的工程扩展思维,又规避了没有企业资质的尴尬。订单状态流转建议用状态机控制,不要把判断逻辑散落在各个Controller里,否则后期维护极其痛苦。
3.4 接口鉴权与登录态处理
小程序登录流程和传统网页登录不太一样:小程序端调用wx.login()获取临时code,传到后端换取openid,后端用openid作为用户唯一标识,同时签发一个自定义的token(比如UUID)返回给前端,前端把token存入storage,之后每次请求在请求头带上。
在Spring Boot端,你只需要加一个拦截器统一校验token:
public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("Authorization"); if (!StringUtils.hasText(token) || !TokenStore.validate(token)) { response.setStatus(401); return false; } return true; } }注册拦截器时要注意,/wx/login、/service/list、/banner/list这些不需要登录就能访问的接口必须放行。我见过太多毕设源码在拦截器配置里把静态资源拦截了,首页图片加载全挂,这就是典型的配置疏忽。学会用excludePathPatterns把白名单维护好,整个项目的健壮性会上一个台阶。
4. 实操全过程:从拿到源码到跑通演示
4.1 本地环境准备
在我看到的很多源码说明文档里,环境准备部分写得比较笼统,这里我给你一个可以直接抄的清单:
- JDK 1.8 或以上版本,Spring Boot 2.x 系列一般要求JDK 8+,我推荐装JDK 8,稳定保守,和大多数毕设源码兼容最好。
- MySQL 5.7 或 8.0,数据库工具用 Navicat 或者 MySQL Workbench 都行。
- 微信开发者工具:稳定版即可,不要用太新的Canary版本,有时会有莫名其妙的编译问题。
- IDEA 或 Eclipse:推荐 IDEA Community,配置少,界面友好。
- Maven 3.6+,用于后端依赖管理。
先用java -version和mvn -v确认环境变量正确,再把SQL文件导入数据库。导入时注意:先创建数据库,执行create database beauty;,再切到该库执行导入脚本。如果你直接整个导出文件导入,容易在目标库里出现库名不一致的问题。
4.2 后端启动与数据库初始化
打开后端工程,第一步修改数据库配置文件。
spring.datasource.url=jdbc:mysql://localhost:3306/beauty?useUnicode=true&characterEncoding=utf8mb4&useSSL=false&serverTimezone=Asia/Shanghai spring.datasource.username=root spring.datasource.password=你自己设置的密码改完后启动主类。如果启动报端口占用,在配置文件里调整:
server.port=8080把端口改成8081、9090都行,但要记得后续小程序端baseUrl要同步修改。后端启动成功后会打印出端口号和项目启动耗时,此时你可以先通过浏览器访问一下某个公开接口,比如http://localhost:8080/api/banner/list,确认能返回JSON数据再做前端联调。
4.3 小程序端配置修改与真机调试
小程序端用微信开发者工具导入项目时,要留意工具会把 AppID 默认读取成你登录账号的AppID,而源码里那个AppID大概率是开发者的,不属于你。这时我会建议你在 详情-基本设置 里选择"测试号",或者不修改AppID直接用游客模式调试。测试号的好处是不涉及个人AppID的权限申请,订阅消息、支付等功能接口也不会因为AppID权限不足而报错。
接着找到封装请求的那个JS文件,比如utils/request.js,把baseUrl改成本机IP加后端端口:
const BASE_URL = 'http://localhost:8080/api';注意,如果你后续用真机预览,localhost就不行了,必须换成你电脑在局域网里的IP,比如http://192.168.1.5:8080/api。手机和电脑要连同一个WiFi,同时后端服务要允许局域网IP访问。通常Spring Boot默认就是0.0.0.0监听,不用额外配置。
这里还有一个新手必踩的坑:微信小程序正式发布要求所有请求域名必须是HTTPS且在后台配置合法域名,但开发调试阶段可以在开发者工具的 详情-本地设置 里勾选"不校验合法域名、web-view(业务域名)、TLS版本以及HTTPS证书"。你如果不勾这一项,每次请求都会报域名不合法,页面一片空白。很多同学拿到源码后怎么调都调不通,十有八九就是卡在这个设置上。
4.4 答辩演示前的几件小事
演示环节最常见的翻车是演示到一半接口挂了。所以提前要做几件事:一是把数据库备份好,演示前恢复到一个干净的数据初始状态,不要把测试的脏数据展示给老师。二是准备一个演示账号和若干条演示数据,比如3个服务项目、2个技师、1个管理员账号,角色分明,讲解时有理有据。三是把后端服务用java -jar的方式打包出来,而不是依赖IDEA里运行,这样可以避免演示时不小心关掉控制台。
mvn clean package -DskipTests java -jar target/beauty-server-0.0.1-SNAPSHOT.jar打包出来的jar包运行方式更接近生产环境,演示时稳定性远比IDE里运行高得多。最后在手机微信上打开小程序真机调试,提前走一遍"浏览服务-预约-支付-查看订单"的完整链路,确认每一步都顺畅。
5. 常见问题与排查技巧实录
5.1 请求拦截器报错与登录失效
症状:首次进入页面时接口能通,但点击某些功能就报401或提示登录过期。原因通常是拦截器把那些需要登录的接口拦下来了,但小程序端storage里的token为空,导致鉴权失败。
排查步骤:先看后端日志,确认是哪个URL被拦截了;再看前端请求头有没有带上Authorization字段;最后看登录接口返回的token有没有正确写入缓存。我建议你在拦截器里加一个白名单,把所有只读类的查询接口全部放行,只有涉及创建订单、修改信息、支付等敏感操作才强制登录。这样既不影响浏览体验,也能让未登录用户正常查看首页和服务列表。
5.2 真机预览白屏或请求超时
这个问题的九成原因都是网络地址不通。在开发者工具里能跑通,一到真机就白屏,先别怀疑代码逻辑,先检查手机能不能访问到电脑的IP。在电脑命令行执行ipconfig查看局域网IP,然后直接用手机浏览器访问http://IP:8080/api/banner/list,看有没有JSON返回。如果超时,检查Windows防火墙是否放行了8080端口,或者系统代理是否干扰了局域网访问。还有一种情况是手机和电脑虽然连的同一个WiFi,但路由器的"AP隔离"功能开启,导致设备之间无法互相访问,这种需要在路由器后台关闭AP隔离。
5.3 图片上传失败与OSS配置
美容服务项目里一定会有图片上传的场景,比如轮播图、服务项目的展示图、用户评价里的晒图。源码里如果使用的是本地存储路径,小程序端上传时会遇到一个问题:前端wx.uploadFile上传成功,但浏览图片时域名加不上或者图片404。因为你上传的图片路径可能是相对路径,而后端返回的图片URL需要拼接上服务器地址。改造方案有两种:一种是后端加一个配置项保存图片访问的基础URL,返回数据时自动拼接;另一种是直接用云存储或者OSS,把上传接口换成上传到对象存储。如果你只是想演示,本地存储就够了,但要注意把application.yml或者application.properties里的上传路径配成一个绝对目录,避免路径越级找不到文件。
5.4 预约时间重叠与并发冲突
很多同学的订单冲突校验只做了数据库查询,这在单机、单用户场景下够用,但严格来说存在并发问题。两个用户同时提交同一个技师同一时段的订单,理论上两个请求都查到数量为0,然后都插入了订单。要在答辩时表现出思考深度,可以在订单表加一个唯一约束(如uk_technician_slot,字段为technician_id + appointment_date + appointment_time),或者用数据库的行级锁来保证同一事务内只能有一个请求成功。如果你不想把逻辑搞复杂,至少要在论文里提到这个隐患并提出解决方案,这在毕业答辩里是很好的加分项。
5.5 答辩被问到的高频问题清单
- 为什么选择微信小程序而不是原生App?答案核心是开发成本低、无需下载安装、微信生态自带流量,同时小程序对个人开发者友好。
- 用户数据安全和隐私怎么保障?可以提到登录凭证是动态
code,数据库密码经过加密存储,传输使用HTTPS。 - 如果预约量很大,系统怎么支撑?可以从数据库索引、分页查询、缓存(Redis)三个方向展开。
- 这个小程序和你之前做的管理系统有什么本质区别?核心就是C端用户体验、移动端适配、微信生态能力调用。
这些问题的答案不用背得滚瓜烂熟,只要你把其中一两个点说到源码里的具体位置,老师就会相信这确实是你自己做的。
6. 这套源码还能怎么扩展出彩
如果你不想止步于"能用"的标准,我给几个低成本但效果明显的扩展思路。第一是接入微信订阅消息,预约成功或订单状态变化时给用户推送通知,这个功能在小程序后台申请模板就能配,前端调用requestSubscribeMessage弹窗请求授权,后端调用订阅消息发送接口,代码量不大,但演示效果非常现代。第二是增加一个数据可视化的小型后台统计页面,用ECharts展示每日预约数、营业额、热门服务项目Top5,这能充分证明你的数据分析能力。第三是给预约订单增加取消理由和超时未支付自动取消机制,用一个定时任务扫描超过15分钟未支付的订单并关闭,逻辑不复杂但非常贴近现实业务。
如果你愿意选择uniapp版本重写前端,还能收获一个加分项:同一套代码以后可以发布到H5和其他小程序平台,但这属于进阶玩法,建议先把原生版本的逻辑吃透再做,不要一开始就陷入框架选型的纠结里。
我在实际带学生做这类项目的过程中感受最深的一点是:拿到源码后不要着急改功能,先把数据流走通。注册一个用户,从前端点击按钮开始,一步步看后端哪个Controller接收了请求,哪个Service处理了业务,哪个Mapper执行了SQL,数据最终落在哪张表里。花一个下午把这条链路走完,你对整个项目的理解程度会超过大多数人,后面的修改和答辩都会变得很轻松。