做毕业设计拿到“基于微信小程序的大学生家教平台”这个题目时,很多同学的第一个念头是:这不就是一个带登录的商品列表吗?前端小程序发请求,后端Spring Boot存数据,把家教当商品挂上去,家长下单就完事了。如果真这么想,那么从需求答辩到代码验收,这条路上会有数不清的坑等着你。家教平台表面上是信息发布与交易,本质上是一套角色分明、流程有状态、权限有边界的供需匹配系统。我见过太多同学把订单状态做成一个String字段,最后在“取消订单后重新接单”这个简单需求上翻车;也见过不少人在小程序左上角的胶囊按钮适配问题上卡了整整一天。这篇文章围绕这个毕设题目,结合我自己做类似项目的经验,把需求拆解、技术选型、核心流程、小程序端易踩的坑、部署上线的路径全部过一遍,希望能帮你少走弯路。
1. 它不只是一套增删改查:家教平台的需求拆解
1.1 角色模型:三种身份,三种权限边界
大学生家教平台最核心的不是“家教”这个实体,而是角色。系统里至少有三种完全不同的身份:
- 家长/学员端:发布家教需求、浏览教员列表、发起约课申请、对已完成订单评价。
- 大学生教员端:维护可授课科目与时段、接收需求推送、接单、更新授课状态、查看授课收入。
- 平台管理员端:审核教员资质、审核需求内容、处理纠纷订单、运营数据统计。
很多毕设只做了前两个角色,管理员端被简化为一个后台登录页加几个列表,这其实是不够的。家教平台具备典型的O2O服务属性,管理员审核是业务闭环里不可或缺的一环。你想想,如果任何一个大学生注册完都能直接接单,家长发个需求也不需要审核,那这个平台跟贴吧有什么区别?所以在需求设计阶段,就要把审核流和状态流转放进去,而不是等项目写到中途再补。
三种角色对应的是三种完全不同的菜单结构和小程序页面。家长端首页是“找老师”,教员端首页是“接订单”,管理员端是Web后台。这些差异必须在设计阶段就确定,否则后面每一项功能都要做身份判断,代码会越写越乱。
1.2 核心业务链路:从发单到结单的完整闭环
用一句话概括家教平台的业务流程:家长发布需求,平台展示合适的教员,教员申请接单,家长确认或系统自动匹配,线下授课,线上结单,双方互评。这条链路里有几个关键设计点:
- 需求发布:家长要填写孩子年级、科目、期望授课方式(上门/线上)、授课区域、预算、期望教员性别/学校层级等条件。注意,这些字段不是简单罗列,它们是后续教员匹配的筛选条件,所以字段设计要结构化,不能图省事塞进一个“备注”里。
- 教员匹配:不一定要做算法推荐,但至少要有按照科目、年级、区域、价格的多条件筛选。更合理的做法是,在发布需求后,系统给符合条件的教员做一个列表推送。
- 接单流程:教员主动申请,家长看到所有申请者后进行选择,或者直接授权系统按评分自动匹配,这才是一个家教平台该有的机制。
- 状态流转:这是最容易被低估的部分。一个完整的家教订单要经历待接单、已接单(待授课)、授课中、已完成、已取消、已退款等状态。每一个状态之间的转换都有前置条件和权限约束。
1.3 数据库表设计:字段和关系要先想明白
按照上面的业务链路,数据库表至少需要这些:
| 数据表 | 核心字段 | 关键说明 |
|---|---|---|
| 用户表 | openid、昵称、头像、手机号、角色类型 | 角色类型建议用tinyint,1家长,2教员,3管理员 |
| 教员信息表 | 用户ID、学校、专业、年级、可授课科目、可授课区域、课时费、教龄 | 与用户表一对一扩展,不是所有用户都有教员信息 |
| 需求表 | 家长ID、科目、年级、区域、预算、期望时间、状态、备注 | 状态覆盖整个需求生命周期 |
| 订单表 | 需求ID、教员ID、家长ID、课时数、总金额、状态、创建时间 | 订单是需求的执行结果 |
| 评价表 | 订单ID、评分、内容、评价人类型 | 家教和学员可以互评 |
| 申诉/反馈表 | 用户ID、订单ID、申诉内容、处理结果 | 管理员介入的入口 |
我的建议是,拿到题目后先画一张E-R图,把表关系理清,再去写代码。我见过不少同学,表设计阶段把“家教”设计成用户表的一个角色字段,后面要做“教员认证资料”、“可授课时间表”的时候,才发现一张用户表根本装不下,最后只能改表结构,连带改所有Mapper,心态直接爆炸。
1.4 需求设计里的几个常见误区
- 误区一:功能堆得越多越好。毕设的评分标准是完整性和正确性,不是功能数量。与其做五个半吊子模块,不如把三个模块做扎实。
- 误区二:忽略了消息通知。家教平台里,家长发完需求后怎么知道有人申请了?教员怎么知道有新需求?虽然毕设大概率没时间做WebSocket和模板消息推送,但至少要在“站内信”或“通知列表”层面留个口子,而不是完全没有。
- 误区三:没有区分“线上支付”和“线下结算”。家教平台的支付是一个敏感且复杂的环节,毕设里不建议接微信支付(涉及商户号、资质审核等),建议做成订单金额展示、线下交易确认的模式,在需求文档里说明这是为演示便捷而做的简化。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型:为什么是Spring Boot + 原生微信小程序
2.1 Spring Boot版本选择:别一上来就追最新版
“springboot版本太高”是同学们搜过的热门词,我自己也踩过这个坑。Spring Boot版本选型直接决定了JDK版本和后续一堆依赖的兼容性,这个选择在项目第一天就要做对。
| 组合方案 | JDK版本 | 适用场景 | 毕设推荐度 |
|---|---|---|---|
| Spring Boot 2.7.x | JDK 8 | 稳定、生态成熟、网上资料多 | 强烈推荐 |
| Spring Boot 3.0+ | JDK 17 | 新特性多、但部分第三方依赖兼容性有坑 | 不推荐 |
为什么推荐Spring Boot 2.7.x而不是3.x?原因很实在:毕设项目不需要Spring Boot 3带来的性能提升,你需要的是搜一个报错信息能搜到海量解决方案。Spring Boot 2.7对应JDK 8,而JDK 8是绝大多数毕设题模板和机房环境默认的JDK。如果你用Spring Boot 3,那么MyBatis-Plus、某些文件上传组件、低版本数据库驱动都会出现兼容性报错,你花在解决这些环境问题上的时间,比写一个完整模块的时间还要多。
2.2 为什么前端不选uni-app而选原生小程序
现在很多同学一听到小程序开发,第一反应就是用uni-app,因为它能一套代码同时编译到微信、支付宝、抖音小程序。但针对这个毕设标题——“基于微信小程序的大学生家教平台”——我更建议使用微信原生小程序开发。理由有三个:
- 原生小程序调试更直接。毕设答辩时,面试官或评委很可能直接打开微信开发者工具看你的代码和真机预览效果,原生小程序的项目结构(pages、components、app.json)一目了然,比uni-app的“中间层”更容易讲清楚。
- 组件和API的使用没有转译问题。原生小程序里调用微信的支付、登录、地理位置等API,返回的数据结构、异常行为都是第一手的,网上搜解决方案时也基本都是原生语法。你搜到一段代码复制过来就能用,而uni-app的语法在编译到小程序端时偶尔会有各种奇怪的兼容问题。
- 毕设侧重点在后端。对于计算机专业的毕业设计,评委更关注的是Spring Boot后端的设计与实现,小程序端只是一个展示载体。用原生小程序能帮助你把精力集中到后端接口设计上,而不是被前端框架的语法细节反复折磨。
当然,如果你本身对Vue非常熟,前端页面量又特别大,用uni-app也不是不行。但如果你是边学边做、以顺利通过答辩为目标,原生小程序是更稳的选择。
2.3 前后端交互约定:统一返回体与API设计
前后端交互是毕设项目里最容易混乱的地方。我的建议是一开始就约定好一个统一返回体,所有接口都返回同样的JSON结构:
java复制public class Result<T> {
private Integer code; // 200成功,400参数错误,401未登录,500服务器错误
private String message; // 提示信息
private T data; // 业务数据
}
这样小程序端网络请求的封装就变得很简单:先判断code,200就取data渲染页面,其他code直接弹Toast提示message。如果每个接口返回结构都不一样,小程序端的逻辑判断会写成一个巨大的if-else,调试起来非常崩溃。
关于RESTful API设计,一句话总结:资源用名词,操作用HTTP方法。例如:
GET /api/demand/list获取需求列表GET /api/demand/{id}获取需求详情POST /api/demand发布新需求PUT /api/demand/{id}修改需求GET /api/teacher/search?subject=math&area=haidian搜索教员
2.4 后端包结构与小程序页面结构
有一个清晰的项目结构,无论是对自己编码还是毕业论文撰写都有很大帮助。下面是我习惯的后端包结构:
code复制com.example.tutor
├── config // 配置类(拦截器、跨域、上传路径等)
├── controller // 接口层,只做参数接收与结果返回
├── service // 业务层,核心逻辑都在这里
├── mapper // 数据访问层(MyBatis-Plus的Mapper接口)
├── entity // 数据库实体类
├── dto // 前端传输对象(接收参数、返回参数)
├── common // 通用工具类(返回体、异常处理、JWT工具等)
└── interceptor // 登录拦截器
小程序端页面结构:
code复制pages
├── index // 首页(根据角色展示不同内容)
├── login // 登录页
├── demand
│ ├── list // 需求列表(教员端查看)
│ ├── publish // 发布需求(家长端)
│ └── detail // 需求详情
├── teacher
│ ├── list // 教员列表(家长端查看)
│ └── detail // 教员详情
├── order
│ ├── list // 订单列表
│ └── detail // 订单详情与状态操作
└── mine // 个人中心
3. 核心流程实现详解:登录、发布、匹配、订单状态机
3.1 微信登录的整体链路:从code到业务Token
微信小程序的登录流程是绕不开的第一关,也是很多同学第一个卡住的地方。整个链路是这样的:
- 小程序端调用
wx.login()获取一个临时凭证code,这个code有效期只有5分钟且只能使用一次。 - 小程序把
code发送到后端接口,例如POST /api/user/login。 - 后端拿着这个
code,调用微信官方的jscode2session接口,换取用户的openid和session_key。这个接口需要用到小程序的appid和appsecret。 - 后端根据
openid在用户表里查找用户,如果找不到就自动注册,如果找到了就更新登录时间。 - 后端生成一个业务Token(推荐JWT,也可以用随机UUID存储到Redis),把这个Token和用户信息返回给小程序。
- 小程序把Token存储到
wx.setStorageSync里,后续所有请求在请求头中带上这个Token。
这里有几个要点需要特别注意。第一,code2Session接口是服务器到服务器的调用,千万不能在小程序端直接拿code去换openid,因为appsecret放在小程序端相当于直接把钥匙交给了别人。第二,JWT里不要放太多东西,包含userId和role就够了,敏感信息一律不放。
java复制@RestController
@RequestMapping("/api/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/login")
public Result<String> login(@RequestBody LoginRequest request) {
// 1. 调用微信接口换取openid
// 2. 根据openid查找用户,不存在则自动注册
// 3. 生成JWT
return Result.success(token);
}
}
3.2 登录拦截器:一个注解搞定权限校验
登录拦截器的作用是保护那些需要登录才能访问的接口。核心逻辑非常简单:在请求头中获取Token,解析出userId,放入ThreadLocal,然后放行。如果Token缺失或校验失败,直接返回401。
java复制@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("Authorization");
if (token == null || !JwtUtil.verify(token)) {
response.setStatus(401);
return false;
}
// 把userId放入ThreadLocal,供后续业务使用
UserContext.set(JwtUtil.getUserId(token));
return true;
}
}
有了拦截器,权限控制就变得非常简单了。需要登录的时候,在Controller的方法上加上@RequireLogin。这里还可以更进一步做角色权限控制,比如教员接单接口只允许教员角色访问,管理员审核接口只允许管理员角色访问。
3.3 需求发布与图片上传
家教需求发布不只是几个文本框提交,它通常包含了一个关键操作——图片上传,比如孩子的试卷照片、教材照片。小程序端的图片上传需要使用wx.uploadFile接口,与普通的wx.request不同,它专门用于文件上传。
后端接收文件的代码:
java复制@PostMapping("/api/upload/image")
public Result<String> uploadImage(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return Result.error("文件不能为空");
}
// 生成唯一文件名,避免重名
String originalFilename = file.getOriginalFilename();
String ext = originalFilename.substring(originalFilename.lastIndexOf("."));
String fileName = UUID.randomUUID().toString().replace("-", "") + ext;
// 保存到指定目录
String uploadPath = "D:/upload/";
file.transferTo(new File(uploadPath + fileName));
// 返回可访问的URL
return Result.success("/upload/" + fileName);
}
上传文件的路径配置是很容易被忽视的一个点。开发时,图片通常存储在本地磁盘,通过Nginx或Spring Boot的静态资源映射来访问。如果项目部署在服务器上,就需要注意存储路径的绝对路径与访问路径的对应关系。建议在application.yml中用自定义配置项维护这个路径,不要硬编码在代码里。
3.4 教员匹配:多条件筛选与排序
教员匹配不要做太复杂的算法,对一个毕设来说,多条件组合查询就足够了。核心查询逻辑是通过科目、年级、区域、价格上限、评分这些动态条件去过滤教员列表,最后再结合课时费从低到高或评分从高到低排序。
使用MyBatis-Plus的LambdaQueryWrapper可以实现简洁的动态条件查询:
java复制public List<Teacher> searchTeachers(TeacherSearchRequest request) {
LambdaQueryWrapper<Teacher> wrapper = new LambdaQueryWrapper<>();
if (StringUtils.hasText(request.getSubject())) {
wrapper.like(Teacher::getSubjects, request.getSubject());
}
if (StringUtils.hasText(request.getArea())) {
wrapper.eq(Teacher::getArea, request.getArea());
}
if (request.getMaxPrice() != null) {
wrapper.le(Teacher::getHourlyFee, request.getMaxPrice());
}
if (request.getMinRating() != null) {
wrapper.ge(Teacher::getRating, request.getMinRating());
}
// 默认按评分倒序
wrapper.orderByDesc(Teacher::getRating);
return teacherMapper.selectList(wrapper);
}
这里要注意,subjects字段如果设计成varchar存字符串(如“数学,英语,物理”),使用like查询是可行的,但性能不算好。作为毕设完全可以接受,但你在论文里应该说明这个取舍,表明你清楚它的局限性。
一个更合理的推荐思路是:当家长发布需求后,系统查询所有符合科目和区域条件的教员,按照评分排序,生成一条“推荐教员列表”写入需求详情页。这样一来,家长打开需求详情时直接看到系统推荐的教员,整体体验比让家长自己从几百个教员的列表里手动筛选要好得多。
3.5 订单状态机:最容易被写崩的地方
订单状态是整个项目里最容易出错的部分,同时也恰恰是最能体现专业素养的设计。一个家教订单的合理状态链是这样的:
code复制待接单 -> 已接单 -> 授课中 -> 已完成 -> 已评价
| | |
| | +---> 取消(超时未授课且家长主动取消)
| +-----------> 取消(教员接单后家长取消)
+---------------------> 取消(发布后无教员接单)
这个状态机的实现方式有很多种,最简单直观的是用一个枚举类(OrderStateEnum)管理所有状态和允许的动作:
java复制public enum OrderStateEnum {
WAITING(0, "待接单"),
ACCEPTED(1, "已接单"),
TEACHING(2, "授课中"),
FINISHED(3, "已完成"),
CANCELLED(4, "已取消");
private int state;
private String desc;
public static boolean canTransit(int from, int to) {
return switch (from) {
case 0 -> to == 1 || to == 4;
case 1 -> to == 2 || to == 4;
case 2 -> to == 3;
default -> false;
};
}
}
canTransit这个方法非常有用。订单状态更新时,先判断当前状态是否允许转换到目标状态,如果不允许,直接抛出业务异常。很多同学写订单更新时直接在Service里写order.setStatus(2),不校验当前状态,结果出现从“已完成”跳回“授课中”这种荒谬数据,答辩时被评委一问一个准。
3.6 数据权限校验:不能只看登录没登录
有登录拦截器还不够,用户登录后能不能操作某个订单,是另一层权限问题。比如订单A属于用户1,用户2去调用“更新订单状态”的接口,系统必须拒绝。这层校验叫数据权限,和前面的登录鉴权是两回事。
最简单的实现方式是在操作前先校验归属关系:
java复制public void updateOrderStatus(Long orderId, Integer newState, Long currentUserId) {
Order order = orderMapper.selectById(orderId);
if (order == null) {
throw new BusinessException("订单不存在");
}
// 校验归属
if (!order.getParentId().equals(currentUserId) && !order.getTeacherId().equals(currentUserId)) {
throw new BusinessException("无权限操作该订单");
}
// 校验状态转换合法性
if (!OrderStateEnum.canTransit(order.getState(), newState)) {
throw new BusinessException("非法的状态转换");
}
order.setState(newState);
orderMapper.updateById(order);
}
这种“数据权限”的校验逻辑看起来简单,但很多同学的实现里根本没有。为什么重要?因为一旦你把本项目部署到公网,别人完全可以遍历订单ID,恶意修改别人的订单状态。作为毕设,这个细节是加分项中非常关键的一个。
4. 小程序开发的几个高频坑位
4.1 顶部导航栏高度:自定义导航栏的适配
热搜词里“微信小程序顶部导航栏高度”是高频搜索,说明这个坑验证了很多同学。默认情况下,小程序页面使用的是系统自带的导航栏,标题文字直接显示在顶部。这种模式不需要适配,但如果你为了页面美观,在app.json或页面的json配置里设置了"navigationStyle": "custom",那么你就需要自己计算导航栏高度。
计算方式如下:
javascript复制const systemInfo = wx.getSystemInfoSync();
const navBarHeight = systemInfo.statusBarHeight + 44;
其中statusBarHeight是状态栏高度(也就是手机顶部的信号栏、时间栏),44px是微信小程序默认导航栏的固定高度(Android和iOS都差不多)。在这里我额外提醒一句:不要用wx.getMenuButtonBoundingClientRect()去动态计算胶囊按钮的位置来做某些相对定位的适配,尤其是在开发工具里和真机上测试结果不一致时,一定要优先以真机为准。
4.2 用户头像昵称获取:新标准和老接口的差异
如果你用的基础库版本较新(2.21.2以上),可能在调用wx.getUserProfile或wx.getUserInfo获取头像昵称时,返回的数据里头像是一个灰色默认头像、昵称是一个“微信用户”。这是微信官方对用户隐私保护的调整,开发者需要改用“头像昵称填写能力”让用户主动选择上传头像和填写昵称。
最简单的处理方式是在个人中心页面放一个表单,用户通过button的open-type="chooseAvatar"和input的type="nickname"来填写资料:
html复制<button class="avatar-wrapper" open-type="chooseAvatar" bind:chooseavatar="onChooseAvatar">
<image class="avatar" src="{{avatarUrl}}" mode="aspectFill"></image>
</button>
<input type="nickname" class="nickname-input" placeholder="请输入昵称" bindinput="onNicknameInput" />
这个改动影响很大。很多教程还停留在老的wx.getUserProfile写法,你照着抄完后发现拿到的就是灰色头像。所以建议从项目一开始就使用新的“头像昵称填写能力”,不要费时间去兼容老接口。
4.3 图片上传:临时路径与服务器地址的切换
wx.chooseMedia或wx.chooseImage选择图片后,返回的tempFilePaths是一个本地临时路径,类似http://tmp/xxx.jpg。这个路径只能在本机调试时使用,一旦换一个设备或页面刷新,路径就失效了。所以需要把临时文件上传到后端服务器,拿到服务器的URL之后,再用这个URL做展示或提交。
上传文件的示例代码:
javascript复制wx.uploadFile({
url: 'http://localhost:8080/api/upload/image',
filePath: tempFilePath,
name: 'file',
success(res) {
const data = JSON.parse(res.data);
// data.data 就是上传后图片的完整URL
}
})
这里再补充一个容易忽略的点:如果使用真机调试,localhost指向的是手机本身,而不是你的电脑。所以后端服务的地址需要配置为你电脑的局域网IP,比如http://192.168.1.100:8080。开发阶段可以把后端允许跨域打开,并把localhost和局域网IP都加入合法域名(开发模式下可以在微信开发者工具中勾选“不校验合法域名”)。
4.4 分页加载:onReachBottom与防重复请求
家教需求列表和教员列表需要做分页。小程序里实现分页加载通常使用onReachBottom这个页面事件,表示用户滚动到页面底部。
javascript复制data: {
page: 1,
pageSize: 10,
list: [],
finished: false,
loading: false
},
onReachBottom() {
if (!this.data.finished && !this.data.loading) {
this.loadMore();
}
},
async loadMore() {
this.setData({ loading: true });
const res = await request('/api/demand/list', { page: this.data.page, pageSize: this.data.pageSize });
if (res.data.length < this.data.pageSize) {
this.setData({ finished: true });
}
this.setData({
list: this.data.list.concat(res.data),
page: this.data.page + 1
});
}
loading标志位的意义在于防止用户在滚动和网络请求返回之间的间隙里连续触发多次请求,导致数据重复。很多同学不做这个标志位,每次滚到底部发两次请求,列表出现重复数据,查半天查不出原因。
4.5 单选框:样式与自定义组件
热搜词里还有“微信小程序单选框”,这个看似简单的组件如果直接使用原生radio,它的圆形样式不好看,而且难以定制。常见改法是用view和image组合做自定义单选效果:
html复制<view wx:for="{{options}}" class="option-item {{selectedIndex === index ? 'active' : ''}}" bindtap="onSelect" data-index="{{index}}">
<view class="radio-circle">{{selectedIndex === index ? '✓' : ''}}</view>
<text>{{item.label}}</text>
</view>
然后用CSS控制.radio-circle的边框、选中态颜色、对勾的显示,这样就能实现完全自定义样式的单选组件。这个思路也适用于城市选择、科目选择、授课方式选择等多个场景。
4.6 模拟器显示正常但真机显示错乱
这类问题十有八九出在样式兼容上。iPhone的底部标签栏(TabBar)高度是34px(home indicator区域),而Android设备没有这个区域,所以你的页面元素如果固定定位在页面底部,在iPhone上就会被手势条遮挡。解决办法是用env(safe-area-inset-bottom)做适配:
css复制.safe-bottom {
padding-bottom: constant(safe-area-inset-bottom); /* 兼容 iOS < 11.2 */
padding-bottom: env(safe-area-inset-bottom); /* 标准写法 */
}
另外,vh单位在部分Android WebView中解析不准确,尽量把固定高度用rpx来表示,确实需要视口高度时可以用windowHeight通过JS计算赋值。
5. 部署与上线:从开发到答辩演示的完整路径
5.1 后端部署:Docker还是裸跑jar包
毕设项目的部署目标是“稳定可演示”,而不是“高并发可用”。因此最简单的方案是直接在服务器上用java -jar运行一个Spring Boot的fat jar。
bash复制java -jar tutor-server.jar --spring.profiles.active=prod
如果你对Docker比较熟,用Docker部署也是加分项。结合热搜词中的“springboot jdk1.8打包到docker desktop”,有些同学用JDK 1.8的Spring Boot项目在Docker里打包时遇到过Exec format error,这通常是镜像的架构和宿主机不一致导致的。例如在x86的电脑上构建,部署到ARM服务器上就会报这个错。解决方法是明确指定镜像的架构,或者直接在目标机器上构建镜像。
我的建议是:优先用裸jar包方式部署。理由很简单,少一层Docker镜像的封装,排查问题就少一个维度。如果你能在答辩时顺口说出“这个项目在服务器上使用systemd守护进程托管,重启时自动拉起”,评委对你的运维意识评价会完全不一样。
5.2 HTTPS与Nginx反代
上线过程中最容易出差错的是HTTPS配置和域名配置。微信小程序有一个硬性要求:所有请求域名必须是HTTPS。开发模式下可以勾选“不校验合法域名”,但一旦提交审核、生成体验版,必须配置合法域名。
使用Nginx作为反向代理是常见的做法。后端Spring Boot跑在8080端口,Nginx监听443端口(HTTPS),并把/api路径的请求转发到后端的8080端口:
nginx复制server {
listen 443 ssl;
server_name api.example.com;
ssl_certificate /etc/nginx/cert/example.pem;
ssl_certificate_key /etc/nginx/cert/example.key;
location /api/ {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
5.3 微信公众平台配置:合法域名与业务域名
打开微信公众平台的后台,在“开发管理-开发设置-服务器域名”中配置三个域名:
request合法域名:配置你HTTPS后端接口的域名。uploadFile合法域名:配置图片上传接口的域名。downloadFile合法域名:配置图片访问的域名。
这三个域名可以配成同一个,前提是根目录下能放微信要求的校验文件。这个校验文件的作用是验证你对域名的所有权,只要通过根目录下的静态文件访问验证,配置即为生效。
5.4 提交审核时的注意事项
小程序提交审核之前,有几个地方必须检查:
- 页面完整性:模拟器中所有按钮都要能正常跳转,不能让审核员在一个页面里点着没反应。
- 类目选择:家教平台属于服务类目,可能需要提供资质材料。如果只是毕业设计,不想走正式的类目审核,可以选择“工具-信息查询”或“教育-教育信息服务”,但审核要求会略有不同。
- 隐私政策:小程序管理后台需要配置用户隐私保护指引,否则涉及用户信息的API调用可能被拒绝。
- 测试账号:提供测试账号的登录方式,便于审核人员体验完整流程。
5.5 答辩演示时的推荐流程
最后说下答辩演示的建议。不要打开页面就开始一顿乱点,而有意识地设计一条演示路径:
- 先用家长身份发布一条家教需求。
- 切换到教员账号,在需求列表里看到这条需求并申请接单。
- 切回家长账号,确认接单,订单状态变为已接单。
- 展示订单状态流转,强调状态机的设计与权限控制。
- 打开数据库或后端日志,展示这条订单数据的前后变化。
这条路线把前端交互、后端接口、数据库变化、业务规则设计完整串起来了,比零散地展示各个页面更有说服力。好的演示是讲述一个业务故事,而不是罗列一堆功能。
另外一个特别重要的细节:演示前关闭开发者工具的缓存模式,清空缓存后重新登录,避免因为之前的接口数据残留引发不一致的问题。这个教训是我自己踩过的,演示到一半发现页面数据是上一轮测试的残留,场面一度非常尴尬。
做这个项目,最核心的收获不是学会了Spring Boot和微信小程序这两个框架本身,而是理解了“把一个真实业务落地成系统”的过程——要从需求里抽象出角色和状态,把模糊的描述转化成明确的数据结构和接口约束,再把一套流程跑通并稳定部署。按照上面的思路走,你的毕业设计不仅能顺利通过,而且能在答辩时从容应对评委对每一个细节的追问。
