做这个“基于微信小程序的学生宿舍寝室管理系统”的时候,我刚带完一届毕业设计,好几个学弟学妹都选了类似的题目。说实话,这个选题在Java毕设里属于非常经典的“中规中矩”型:技术上不冒进,用的全是主流且成熟的东西;业务上也不冷门,宿舍管理每个学校都有真场景,好调研、好画图、好答辩。但越是这样“烂大街”的题目,越能看出一个人是不是真的把系统做扎实了。
很多同学拿到这种题目后的第一反应是上网找源码,找到后能跑起来就万事大吉。但我得说句实在话:如果你只是把源码跑通,却讲不清楚为什么这样设计表结构、为什么这个接口要这样写、为什么小程序要这样通信,那答辩的时候老师几句话就能把你问住。这篇文章我就把这个项目的完整设计思路、核心实现、实操过程、以及我踩过的坑全部梳理一遍,既能帮你把这个系统真正吃透,也能让你在面试、答辩的时候有东西可讲。
1. 项目整体设计与需求拆解
1.1 宿舍管理系统到底在“管理”什么
很多人拿到题目第一件事就是开写代码,结果写了一半发现这里缺个功能、那里逻辑对不上。我习惯先做一件事:把“宿舍管理”这个笼统的概念拆成具体的业务场景。
一个学生宿舍管理系统,核心要管的无非是这几块:
- 基础档案:楼栋、寝室、床位、学生的基本信息。这是所有功能的基石,没有这套档案,后面全是空谈。
- 住宿分配:学生入住哪个楼栋、哪间寝室、哪个床位;调宿、退宿、毕业离校。这是宿舍管理最核心的日常操作。
- 日常事务:报修(灯坏了、水龙头漏水)、卫生检查打分、晚归/夜不归宿记录、来访登记、请假外出登记。
- 信息发布:公告通知(停水停电、宿舍楼活动、安全检查通知),管理员发,学生看。
拆完之后你会发现,这个系统的业务边界很清楚:它不是一个ERP,也不是一个OA,它的核心就是“人-床位”的关系管理外加围绕宿舍发生的各类事务记录。
搞清楚要做什么之后,再想另一个问题:谁在用这个系统?这个问题直接决定了你的权限设计。
1.2 三类角色的权限设计思路
我见过很多学生的项目,登录之后所有页面都能访问,管理员能做的学生也能做,这种在答辩时最容易被挑毛病。这个系统我按最常见的做法,设计了三个角色:
| 角色 | 核心诉求 | 典型操作 |
|---|---|---|
| 学生 | 查寝、报修、看公告 | 查自己住的寝室、提交报修、查看公告通知 |
| 宿管员 | 管楼栋、处理事务 | 分配寝室、处理报修工单、录入卫生检查、登记晚归 |
| 系统管理员 | 管人、管基础数据 | 维护楼栋寝室、维护学生账号、管理宿管账号、查看统计报表 |
这三个角色之间的数据隔离和权限控制,是答辩时老师最喜欢问的点。比如学生只能看到自己所在寝室的信息,宿管只能看到自己管辖楼栋的数据,管理员拥有一切权限。这个在实现上,就是后端接口里做角色判断和行级数据过滤,不能只靠前端藏按钮就完事。
1.3 为什么选SpringBoot+微信小程序这个组合
先说明一下选型的理由,因为答辩老师一定会问“你这个技术选型是怎么考虑的”。
后端选SpringBoot,理由很直白:它是目前Java Web开发的事实标准。配置简化、内嵌Tomcat、生态成熟、招人好招,而且作为学生项目,用SpringBoot能充分展示你对Java生态的掌握程度。很多同学纠结“要不要用Spring Cloud”,我建议不要,单体应用就够,微服务是给自己找麻烦。
前端选微信小程序,理由也很实际:现在学生手机上都有微信,小程序免安装、即开即用,比做一个App再去发安装包省事得多,也比传统B/S模式下的手机浏览器访问体验好。而且小程序开发门槛低,用原生框架或者uni-app都行,对于个人开发者来说非常友好。
这个组合的另一层考虑是前后端分离:小程序端只负责展示和交互,所有的业务逻辑都放在后端API里。这种架构在答辩时能讲出东西来,也符合现在企业里主流的开发模式。
2. 核心细节解析与实操要点
2.1 版本选型:SpringBoot到底用哪个版本
这里必须单独拿出来说,因为版本问题是这个项目里最容易踩的坑。我在带学生的过程中,至少有三分之一的人卡在版本不兼容上。
SpringBoot 3.x已经出来很久了,但我的建议是:如果你的JDK是8,老老实实用SpringBoot 2.7.x。为什么?SpringBoot 3.x最低要求JDK 17,而且很多第三方库(尤其是国内一些框架)对3.x的适配还不完善。你做个毕设,没必要在这个地方给自己添堵。
依赖版本统一管理方面,我建议直接用Spring Initializr生成项目骨架,不要自己手动去拼版本号。生成的时候注意这几个核心依赖:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> </parent> <dependencies> <!-- Web核心 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- MyBatis-Plus,后面生成代码能省很多事 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.2</version> </dependency> <!-- MySQL驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <!-- Lombok,简化实体类 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- JWT做登录态 --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.5</version> </dependency> </dependencies>注意:网上很多旧教程用的是SpringBoot 1.x或2.0.x,那些教程里的配置方式放到2.7版本上很多已经变了,比如
application.yml里的spring.mvc配置、MyBatis-Plus的分页插件写法等。别拿着旧教程硬套新版本,版本不一致出现的异常信息非常迷惑人。
2.2 数据库设计:表结构要经得起推敲
数据库设计是这个项目能不能拿高分的关键。我见过太多人设计表的时候,要么字段少的可怜,要么表之间关联混乱。这里我把我整理的表结构核心部分列出来,大家可以直接参考。
用户表(user):存储所有登录用户,用一个role字段区分是学生、宿管还是管理员。
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| username | varchar | 登录账号(学号/工号) |
| password | varchar | 密码(BCrypt加密) |
| role | tinyint | 0-学生,1-宿管,2-管理员 |
| real_name | varchar | 真实姓名 |
| phone | varchar | 手机号 |
| avatar | varchar | 头像URL |
| status | tinyint | 是否禁用 |
楼栋表(dorm_building):宿舍基本单元。
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| name | varchar | 楼栋名,如"一号公寓" |
| manager_id | bigint | 宿管用户ID,关联user表 |
| floors | int | 楼层数 |
| gender | tinyint | 男生/女生楼栋 |
寝室表(dorm_room):承载实际的管理逻辑。
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| building_id | bigint | 所属楼栋 |
| room_number | varchar | 房间号 |
| capacity | int | 可住人数,如4人间 |
| current_people | int | 当前已住人数 |
| bed_count | int | 床位数量 |
| status | tinyint | 空闲/部分入住/已满 |
学生住宿表(student_dorm):这是核心关联表,记录“谁住在哪里”。
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| student_id | bigint | 学生用户ID |
| room_id | bigint | 寝室ID |
| bed_number | varchar | 床号(如A、B、C、D) |
| check_in_time | datetime | 入住时间 |
| check_out_time | datetime | 退宿时间,为空表示正在住 |
这里有个关键点:一个学生同时只能有一条check_out_time为空的记录,这是业务上的硬约束,需要在代码里校验,不能只靠数据库层面兜底。很多项目一开始没想到这点,学生就能同时出现在两个寝室里。
报修表(repair_order):宿舍管理里最高频的日常事务。
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| student_id | bigint | 报修人 |
| room_id | bigint | 报修寝室 |
| content | varchar | 问题描述 |
| image | varchar | 图片URL,小程序端可选上传 |
| status | tinyint | 0-待处理,1-处理中,2-已完成 |
| create_time | datetime | 提交时间 |
| handle_time | datetime | 处理完成时间 |
卫生检查表(hygiene_check)、公告表(notice)相对简单,核心几个字段够用就行,不赘述。
这套表设计完,你会发现所有业务场景都能跑通:查一个学生住哪 → 从student_dorm关联dorm_room再关联dorm_building;查一个楼栋的入住率 → 统计该楼栋下所有寝室的current_people/capacity。
2.3 小程序登录:wx.login到token的完整链路
这个点是热点里的高频词,也是很多新手很懵的地方。微信小程序不像网页那样有传统的账号密码session机制,它有自己的登录体系。整个链路是这样的:
- 小程序端调用
wx.login(),微信会返回一个临时登录凭证code。 - 小程序把
code发给后端。 - 后端拿着
code加上小程序的appid和secret,去微信的接口(https://api.weixin.qq.com/sns/jscode2session)换openid和session_key。 - 后端拿到
openid后,去用户表里查这个openid是否已绑定学生账号;如果绑定了,生成一个JWT token返回给前端;如果没绑定,返回一个标识让前端跳转到绑定页面。 - 小程序把token存到
wx.setStorageSync('token', xxx),后续所有请求都在header里带上Authorization: Bearer xxx。
但注意一个细节:大多数毕设项目还有一个“账号密码登录”的入口,因为不可能所有学生都用微信登录,而且老师演示的时候用账号密码更快。我做的方案是:
- 登录页有“微信一键登录”和“账号密码登录”两个tab。
- 账号密码登录直接校验用户名密码,成功后签发token。
- 微信登录走上面5步链路,如果是新用户会引导绑定手机号或学号。
关于token的有效期,我建议设置成7天,既保证用户体验,又不会因为过期太频繁让评委觉得系统麻烦。JWT实现时记得加上过期时间校验,过期后前端统一跳回登录页。
3. 实操过程与核心环节实现
3.1 后端项目结构与统一返回结果
项目结构我按照教科书式的分层来做,既清晰又好答辩:
com.example.dorm ├── controller // 接口层,只做参数接收和结果返回 ├── service // 业务层,写具体业务逻辑 │ └── impl ├── mapper // 数据访问层,mybatis接口 ├── entity // 实体类 ├── dto // 请求参数对象 ├── vo // 返回结果对象 ├── config // 配置类(拦截器、跨域、MyBatis-Plus等) ├── common // 公共类(统一返回结果、异常处理、常量) └── utils // 工具类(JWT工具、密码加密等)统一返回结果是正规项目必备的规范。很多新手喜欢直接用Map返回数据,字段名靠猜,前端拿数据全靠运气。我的做法是定义了一个Result<T>类:
@Data public class Result<T> { private Integer code; // 200成功,500失败,401未登录 private String message; // 提示信息 private T data; // 业务数据 public static <T> Result<T> success(T data) { Result<T> result = new Result<>(); result.setCode(200); result.setMessage("操作成功"); result.setData(data); return result; } public static <T> Result<T> error(String message) { Result<T> result = new Result<>(); result.setCode(500); result.setMessage(message); return result; } public static <T> Result<T> unauthorized() { Result<T> result = new Result<>(); result.setCode(401); result.setMessage("未登录或登录已过期"); return result; } }前端小程序端就统一判断code == 200再做业务处理,401就跳登录页。这样一套规范下来,联调的时候会省非常多的口舌。
3.2 JWT登录态管理:全局拦截器怎么配
JWT是这个系统中用来管理登录态的核心工具。实现逻辑很简单:登录成功后,后端生成一个token返回给前端,前端每次请求都带上它,后端拦截器负责校验。
生成token的工具类核心代码:
public class JwtUtils { private static final String SECRET = "your-secret-key-for-jwt-signature"; private static final long EXPIRE = 7 * 24 * 60 * 60 * 1000L; // 7天 public static String generateToken(Long userId, String role) { return Jwts.builder() .setSubject(String.valueOf(userId)) .claim("role", role) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + EXPIRE)) .signWith(SignatureAlgorithm.HS256, SECRET) .compact(); } public static Claims parseToken(String token) { return Jwts.parser().setSigningKey(SECRET) .parseClaimsJws(token).getBody(); } }配置拦截器统一校验:
@Component public class JwtInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行登录接口和微信code2session接口 if (request.getRequestURI().contains("/auth/")) { return true; } String authHeader = request.getHeader("Authorization"); if (authHeader != null && authHeader.startsWith("Bearer ")) { try { String token = authHeader.substring(7); Claims claims = JwtUtils.parseToken(token); request.setAttribute("userId", Long.parseLong(claims.getSubject())); request.setAttribute("role", claims.get("role")); return true; } catch (Exception e) { // token无效或过期 } } // 返回401 response.setStatus(401); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write("{\"code\":401,\"message\":\"未登录或登录已过期\"}"); return false; } }注意:不要只拦截“需要登录的接口”,也不要全部拦截。我的习惯是所有
/api/**都过拦截器,在拦截器内部判断哪些是白名单(比如登录、注册、微信code2session),其他的必须带合法token。这样以后新增接口默认就是需要登录的,不会漏配。
3.3 寝室分配与床位管理:核心业务逻辑
寝室分配是整个系统里最能体现“你确实理解了业务”的地方。它涉及两个表的状态同步更新:学生住宿表新增一条记录,同时寝室表的current_people要 +1;退宿则相反。
但要注意一个细节:床位分配不能只选寝室不选床位。一个4人间住了3个人,新来的学生应该被分配到空着的那个床位,不能随便挤掉别人。我在前端做的是:调接口查询某个寝室的床位占用情况,返回哪些床位空着,学生或宿管选择具体床位后提交。
后端分配住宿的核心逻辑可以这样写:
@Transactional public Result assignRoom(AssignRoomDTO dto) { // 1. 校验学生是否存在且未在住宿 StudentDorm existing = studentDormMapper.findActiveByStudentId(dto.getStudentId()); if (existing != null) { return Result.error("该学生当前已住在 " + existing.getRoomId() + " 号寝室,请先办理退宿"); } // 2. 校验寝室是否存在且未满 DormRoom room = roomMapper.selectById(dto.getRoomId()); if (room == null) { return Result.error("寝室不存在"); } if (room.getCurrentPeople() >= room.getCapacity()) { return Result.error("该寝室已住满"); } // 3. 校验床位是否被占用 Long count = studentDormMapper.countBedOccupied(dto.getRoomId(), dto.getBedNumber()); if (count > 0) { return Result.error("该床位已被占用,请更换床位"); } // 4. 写入住宿记录 StudentDorm sd = new StudentDorm(); sd.setStudentId(dto.getStudentId()); sd.setRoomId(dto.getRoomId()); sd.setBedNumber(dto.getBedNumber()); sd.setCheckInTime(new Date()); studentDormMapper.insert(sd); // 5. 更新寝室当前人数 room.setCurrentPeople(room.getCurrentPeople() + 1); roomMapper.updateById(room); return Result.success(null); }这里用@Transactional很关键——如果只看前面几步,一旦第5步失败,学生住宿记录已经写进去了,数据就不一致了。加事务保证“要么全部成功,要么全部失败”。
3.4 小程序端实现:从请求封装到页面组织
小程序端的开发,我推荐直接用微信开发者工具配合原生框架写。网上有很多人推荐用uni-app或者Taro,但如果是毕设项目,原生写法的调试成本最低,遇到问题搜索引擎随便一搜就是答案。
小程序的目录结构:
pages/ ├── index/ // 首页:轮播图+公告 ├── dorm/ // 我的寝室:查看房间信息、室友 ├── repair/ // 报修:提交报修、查看记录 ├── login/ // 登录页 ├── mine/ // 个人中心 utils/ ├── request.js // 统一请求封装 ├── auth.js // 登录态管理统一请求封装是每个小程序项目必备的工具。原生的wx.request每次都要写一堆重复代码,我封装了一个request.js:
const BASE_URL = 'http://localhost:8080/api'; function request(path, method, data) { return new Promise((resolve, reject) => { wx.request({ url: BASE_URL + path, method: method || 'GET', data: data || {}, header: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + wx.getStorageSync('token') }, success: (res) => { if (res.statusCode === 401) { // 登录过期,跳转登录页 wx.navigateTo({ url: '/pages/login/login' }); reject(res.data); return; } resolve(res.data); }, fail: (err) => reject(err) }); }); } module.exports = { get: (path, data) => request(path, 'GET', data), post: (path, data) => request(path, 'POST', data), put: (path, data) => request(path, 'PUT', data), delete: (path, data) => request(path, 'DELETE', data) };这里有个坑要提一下:真机调试时localhost会指向手机本身,不是你的电脑。所以你用真机预览的时候,BASE_URL要改成你电脑在局域网里的IP,比如http://192.168.1.100:8080/api。而且微信开发者工具里要勾选“不校验合法域名、web-view(业务域名)、TLS版本以及HTTPS证书”,不然请求会被拦截。这个配置在“详情-本地设置”里。
3.5 后台管理的简单实现思路
有些学生会纠结要不要单独做一个PC后台管理端。我的建议是:如果时间和精力有限,可以做一个简单的Web管理端,或者不做PC端,把管理功能全部通过小程序端管理员入口完成。
但如果要做一个PC后台,最简单的路子是:SpringBoot只负责提供API,前端拿一个现成的后台模板套上去就行。像vue-element-admin、若依(RuoYi)这类开源后台管理系统,自带用户管理、菜单管理、权限管理,非常适合快速搭建管理后台。
不过要提醒一点,如果你用了若依这类现成的后台框架,你基本要研究一遍它的权限系统和工作原理,不然改起来会很痛苦,答辩也容易露馅。
4. 常见问题与排查技巧实录
4.1 版本恶魔:JDK、SpringBoot、MyBatis-Plus三方版本不兼容
这个问题几乎每次带学生都会遇到,症状千奇百怪:有些是启动直接报错ClassNotFoundException,有些是启动正常但查询报错,有些是Lombok生成的getter/setter找不到。
我的排查思路是三步:
- 先确认JDK版本。命令行执行
java -version,如果是JDK8,SpringBoot必须用2.x;如果是JDK17+,可以用SpringBoot 3.x。 - 确认Maven依赖树。
mvn dependency:tree看有没有冲突的版本。最常见的坑是MyBatis-Plus和SpringBoot版本跨度太大出现兼容问题,比如MyBatis-Plus 3.x对SpringBoot 3.x支持还不完善。 - 启动日志报错最关键。很多同学报错后只看异常的最后一行,其实前面几十行里才有真正的原因,比如“Caused by”后面的内容。
这类问题的标准解法是:在pom.xml里指定所有核心依赖的版本号,不要依赖传递。实在不行就删掉本地Maven仓库(
~/.m2/repository),重新mvn clean install,能解决80%的依赖疑难杂症。
4.2 小程序请求不到后端:跨域、IP、域名校验
联调阶段最常见的报错是“request:fail”或者控制台直接显示请求被拦截。这里挨个排查:
跨域(CORS)问题:小程序请求后端时,后端需要允许跨域。我习惯写一个CorsConfig:
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }真机IP问题:刚才说过的,真机调试必须用电脑的局域网IP,不能是localhost。实在不知道怎么查IP,命令行执行ipconfig(Windows)或ifconfig(Mac/Linux),找到IPv4地址。
开发者工具校验问题:本地调试时,在微信开发者工具的“详情 → 本地设置”里勾选“不校验合法域名”。但如果要做上线部署,这个选项就没了,必须配置HTTPS域名。
4.3 Token失效的处理和小程序登录态更新
很多新手遇到token过期就懵了,不知道怎么处理。前端全局处理的原则是:任何一个接口返回401,就代表登录态失效,统一跳登录页,不要每个页面单独写。
换一种更优雅的方案是:请求封装里拦截401 → 静默调用wx.login()重新获取code → 调后端/auth/renew刷新token → 用新token重发原请求。但这个方案实现复杂度高一些,毕设阶段用跳转登录页的方案已经够用。
4.4 常见问题速查表
| 现象 | 可能原因 | 解决思路 |
|---|---|---|
| 项目启动报端口被占用 | 8080端口被其他进程占用 | `netstat -ano |
| 数据库连接失败 | MySQL未启动/密码不对 | 先确认MySQL服务是否启动;检查yml里密码是否和本地一致 |
| 查询返回中文乱码 | 连接串没有设编码 | JDBC URL加characterEncoding=utf8 |
前端报undefined | 接口返回结构和小程序取数据路径不一致 | 统一返回Result结构,前端统一用res.data.data取业务数据 |
| MyBatis-Plus分页不生效 | 没配置分页插件 | 加MybatisPlusInterceptor并添加PaginationInnerInterceptor |
| 小程序setData报错 | 数据没深拷贝 | 先JSON.parse(JSON.stringify(data))再setData |
| 上传图片失败 | 后端没配Multipart大小 | spring.servlet.multipart.max-file-size和max-request-size调大 |
4.5 数据处理:初始化数据的妙用
很多同学交出项目的时候数据库里就一两行测试数据,演示的时候要临时造数据,页面看起来空荡荡的。我的习惯是准备一份完整的初始化SQL脚本,里面包括:
- 3栋楼(含不同性别、不同楼层)
- 每栋楼3-5层,每层若干间寝室(含2人间、4人间、6人间)
- 20-30个学生账号(密码统一设为123456)
- 每个学生分配到具体的寝室和床位,模拟出部分寝室已满、部分有空位的状态
- 几条不同状态的报修工单(待处理、处理中、已完成)
- 几条不同日期的卫生检查记录
- 几条公告(含置顶公告)
这样做的好处是:演示的时候可以直接展示各种“状态”的效果,而不是现场操作半天才能看到数据变化。而且这份SQL脚本本身也是交付物的一部分,文档里写清楚怎么导入,评委一看就觉得你做事很完整。
5. 这个项目还能怎么扩展
最后分享一点我对这个项目的后续扩展思考,尤其是如果你打算把它作为面试项目或者求职作品,这些扩展点非常加分。
数据可视化:在管理后台增加统计报表,比如按楼栋统计入住率、按月统计报修数量、卫生检查平均分趋势图,用ECharts画几个图表出来,立刻让整个系统的“技术含量”上一个档次。
消息推送:报修处理完成时,通过小程序的订阅消息(wx.requestSubscribeMessage)给学生发通知。这个功能在答辩现场演示出来很震撼,因为它涉及小程序开放能力,是很多学生项目没有做过的点。
Redis缓存:把公告列表、楼栋信息等热点数据缓存到Redis,减少数据库压力。面试时如果被问到“缓存怎么做”,你可以直接拿这个项目举例。加一个简单的Redis缓存,就能在简历上写“熟悉Redis在SpringBoot项目中的应用”。
Nginx部署:把前端打包后的静态资源和后端API用Nginx做反向代理,一台服务器搞定整个项目的部署。这也是面试常问的部署方案。
但我的真心建议是:先把这个基础版本做扎实,再谈扩展。因为很多同学连基础的增删改查和数据一致性都没处理好,就急着加Redis、加消息队列,最后演示的时候连主流程都跑不通,得不偿失。
6. 写在最后的经验和建议
这个项目我从大四开始第一次做,到现在带过好几届学生做类似的题目,每个阶段感受都不一样。最初我拿到题目时,脑子里就两个字:“凑合”——能跑就行,能用就行。但后来被答辩老师问了几次“你这个字段设计的依据是什么”“如果并发两个学生同时抢最后一个床位怎么办”,我才真正意识到:做这种“经典题目”最大的价值不在于代码量,而在于你能不能想清楚每个设计背后的理由。
比如床位并发的那个问题,我当时的回答是“用数据库的唯一约束+事务”。后来想了想,其实更稳妥的方案是给床位分配接口加分布式锁,但作为单体应用,用synchronized或者数据库悲观锁也够了。这种思考过程比代码本身更能让你成长。
最后给一个非常实用的建议:把项目运行起来后,写一份“部署文档”。不是给老师的那种论文式文档,而是给自己看的,记录从拉代码到跑起来的所有步骤、踩过的坑、怎么解决的。这份文档在你自己调试的时候能省大量时间,甚至在面试的时候还能拿出来讲一讲你遇到什么问题、怎么排查的,这本身就是面试官很喜欢听的“项目经历”。
这个系统做下来,你对SpringBoot、MyBatis-Plus、小程序开发、JWT认证这套全栈技术栈的理解会扎实非常多。如果每一步都亲自动手、真正吃透,无论是答辩还是面试问到相关知识点,你都能胸有成竹地回答。