每年到毕业季,我都会收到不少学弟学妹的私信,问“毕设做什么题目好”“SpringBoot和Android能不能结合”“有没有完整能跑的源码”。如果你正在被这些问题困扰,那么基于SpringBoot+Android的民宿预订系统是一个非常值得考虑的选题——它既有后端业务逻辑的复杂度,又有移动端界面与交互的呈现,还踩中了当下旅游住宿数字化的大众需求。
这套系统的典型能力包括:用户注册登录、房源浏览与搜索、房间详情查看、在线下单预订、订单状态管理(待支付/已支付/已入住/已退房)、个人中心与消息提醒,配合后台管理端还可以实现房源管理、订单管理、用户管理等功能。麻雀虽小,五脏俱全,无论是用来完成本科毕设,还是作为求职简历里的完整项目,都非常有说服力。
本文不打算做泛泛的“项目介绍”,我会把这套民宿预订系统从选题逻辑、数据库设计、接口规划,到Android端实现过程中的核心代码片段、联调注意事项、论文写作思路,以及我亲测踩过的一些坑,一次性拆开揉碎讲明白。如果你正准备开始动手,或者已经写了一半卡在某个环节,这篇文章应该能帮你省下不少时间。
1. 毕设选型:为什么是SpringBoot + Android民宿预订系统
1.1 选题价值与技术栈合理性分析
先解决一个根本问题:毕设题目那么多,为什么要选民宿预订?我对这个题目的判断是,它的复杂度刚刚好——既不会简单到让答辩老师觉得你没干活,也不会复杂到让一个在校生在几个月内无法完成。
从业务层面看,民宿预订天然具备一套完整但边界清晰的业务闭环。用户端需要注册登录、筛选房源、查看房型、提交订单、模拟支付、个人中心;管理端需要房源的增删改查、订单审核与状态流转、基础数据统计。这个闭环覆盖了绝大多数Web/App系统都有的核心流程:基于角色的权限区分、用户与房源的关联、订单状态的可迁移性。相比之下,做一个“图书管理系统”或“学生管理系统”虽然也能跑通,但业务太单薄,答辩时容易陷入“为什么这么简单”的尴尬;而做电商平台、大型社交软件,又会陷入订单拆分、消息推送、高并发的泥潭中,不适合在校生的时间安排。
从技术选型看,SpringBoot + Android的组合在近几年的毕设题目里热度一直很高,原因也很实在。SpringBoot是目前Java后端开发的事实标准,它简化了Spring的配置流程,内嵌Tomcat容器,搭配MyBatis-Plus可以快速完成数据持久化层的编码。Android端则用Java或Kotlin开发原生App,不依赖第三方小程序平台,数据交互走RESTful API,技术点清晰、可控性强。一套代码下来,把Spring生态、Android生命周期、数据库设计、HTTP通信、JSON解析这些知识点全部串起来,正好覆盖面试官和答辩老师最看重的基础能力。
提示:部分学校对毕设题目中的技术栈有“新旧程度”的隐性要求。SpringBoot 2.x + Android(Java)是目前兼容性最好的组合,网上资料最多,遇到问题也最容易搜到解决方案。如果你手里已经装好了Android Studio和JDK,建议沿用这套组合,不要贸然挑战新框架组合,时间成本往往比想象中高。
1.2 面向的用户角色与核心业务场景
不少同学在做设计的时候,花了很多精力在技术细节上,却没有把用户角色和业务场景梳理清楚,导致后面的功能设计东一榔头西一棒子。这里我把自己常用的一套分析思路分享出来。
这套民宿预订系统的使用场景可以简化为三个角色:
- 游客/未登录用户:可以浏览首页推荐房源、查看房源详情,但在提交订单前必须注册或登录。这个设定在答辩时很加分,说明你考虑到了数据归属问题,而不是“都能看都能买”的粗放设计。
- 注册用户(C端消费者):登录后可以搜索房源、筛选价格区间、查看房间图片和设施列表、提交订单、模拟支付、查看订单状态、发起退订,还可以在个人中心修改头像和昵称。
- 管理员(B端运营人员):通过单独的管理端入口登录,可以维护民宿房源(上架、下架、编辑价格和库存)、处理订单状态(确认入住、标记退房)、查看注册用户列表。
常见业务场景重复度高,但这反而是好事:一套代码的核心逻辑可以被反复调用,你不需要为了每一个功能从零搭建。比如“根据城市搜索民宿”、“根据价格区间筛选”、“根据入住日期判断房间是否可订”,这些本质上都是SQL查询条件组合的问题;订单状态流转在数据库中其实就是status字段的更新,配合创建时间和更新时间来做记录。
1.3 数据库设计先行:核心表结构与关系说明
我特别想强调一点:做这种前后端分离的毕设项目,永远先设计数据库,再写代码。我见过太多同学先写接口、后改表结构,结果到联调阶段发现字段对不上,改起来想砸电脑。
这张表设计了几个核心表,数据结构直接粘贴一份常见的模板供参考:
- 用户表(user):用户ID、昵称、手机号(登录账号)、密码(MD5加密存储)、头像URL、角色标识(普通用户/管理员)、创建时间。
- 民宿表(homestay):民宿ID、名称、所在城市、详细地址、封面图URL、简介、价格(元/晚)、可订房间数、设施列表(用JSON字符串或逗号分隔存储)、状态(上架/下架)、创建时间。
- 订单表(order):订单ID、订单编号(唯一,便于展示和检索)、下单用户ID、民宿ID、入住日期、退房日期、入住人数、订单金额、状态(待支付/已支付/已取消/已入住/已退房)、创建时间、支付时间。
这几个表之间的关系也比较直观:一个用户可以下多个订单,一个民宿可以被多个订单关联,订单表中冗余民宿名称和价格快照(防止民宿信息修改后订单数据失真),这就是典型的订单快照设计,面试的时候可以拿出来说。
数据库创建时建议用utf8mb4字符集,否则保存民宿简介里的特殊符号和emoji时会报错。这一点在后面Android端传数据时尤其容易出现,我后面会专门提到。
2. 后端SpringBoot设计与开发要点
2.1 项目初始化与必要依赖引入
SpringBoot项目的创建方式很成熟,直接在IDEA里通过Spring Initializr创建即可,或者去官网start.spring.io生成压缩包再导入,效果一样。这里给出pom.xml中必要的核心依赖列表,大家可以对照检查自己是否漏引。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>如果你不想用MyBatis-Plus,用Spring Data JPA也行,但MyBatis-Plus的BaseMapper内置了常用的增删改查方法,分页插件用起来也方便,适合快速完成项目。实际开发中你会发现,80%的数据库操作用内置方法就够了,只有少数复杂统计查询需要手写SQL。这一点可以写进论文作为“技术选型理由”。
2.2 用户登录与JWT Token鉴权
登录鉴权是几乎所有C端项目的核心。民宿预订系统里,Android端每次请求需要携带当前登录用户的身份凭证,这里我选了JWT(JSON Web Token)方案,而不是传统的Session。原因是Android端不像Web端天然支持Cookie自动携带,JWT放在请求头里更灵活,而且用SpringBoot实现起来也不复杂。
我设计了一个简单的结构:
- 用户登录成功后,后端用用户ID和角色标识生成Token,设置过期时间(例如2天)。
- 前端每次请求时在Header里附带
token字段。 - 后端用一个拦截器(HandlerInterceptor)统一校验,放行登录接口和房源列表接口,其余接口全部要求登录。
关于密码处理,这里多说一句:别用明文存储。我建议至少用MD5加盐的形式,salt可以取用户手机号后四位或固定的盐值字符串。如果你动手能力强,可以用Spring Security的BCryptPasswordEncoder,但相对会多一些配置工作量。对毕设来说,MD5加盐是性价比最高的方案,答辩时也能说得清楚。
// 简单示例:拦截器中校验token public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("token"); if (StringUtils.isBlank(token)) { response.setStatus(401); return false; } // 解析token,失败则返回401 try { Claims claims = JwtUtil.parseToken(token); request.setAttribute("userId", claims.get("userId")); return true; } catch (Exception e) { response.setStatus(401); return false; } }2.3 核心接口设计与响应格式统一
接口设计看似是各写各的,但如果没有统一规范,到了Android端对接时会非常痛苦。我常用的做法是定义统一返回体Result,包含三个字段:code(状态码)、message(提示信息)、data(业务数据)。
@Data public class Result<T> { private Integer code; 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(Integer code, String message) { Result<T> result = new Result<>(); result.setCode(code); result.setMessage(message); return result; } }这样Android端拿到响应后,只要判断code是否等于200就能确定请求是否成功。常见的code设计为:200成功、401未登录、500服务器异常、400参数错误。
核心接口清单大概如下:
- POST /api/user/register:注册
- POST /api/user/login:登录
- GET /api/homestay/list:民宿分页列表(支持关键字/城市/价格筛选)
- GET /api/homestay/detail?id=xxx:民宿详情
- POST /api/order/create:创建订单
- GET /api/order/list?userId=xxx:查看我的订单
- PUT /api/order/cancel:取消订单
- POST /api/admin/homestay/save:管理员新增/编辑房源
- GET /api/admin/order/list:管理员查看订单列表
写接口时注意一个细节:列表接口务必做分页,哪怕是毕设也要传页码page和每页数量pageSize。否则数据量稍大,Android端一次性加载所有数据会明显卡顿,答辩现场很尴尬。MyBatis-Plus的分页插件配置网上随处可见,不赘述。
2.4 后端部署前的配置项说明
application.yml中关键配置项要写全,尤其是日期格式、Jackson序列化、数据库连接参数。很多同学联调时接口返回的时间是一串数字(时间戳),就是因为在yml中没设置日期格式。
spring: datasource: url: jdbc:mysql://localhost:3306/homestay_db?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl务必在url里加上useUnicode=true&characterEncoding=utf8mb4,很多中文乱码、emoji无法存储的问题,源头就在这里。
3. Android端App设计、实现与联调
3.1 项目结构划分与基础封装
Android端的包结构我建议按模块划分,而不是按类型堆叠,否则后期找文件找到怀疑人生。
比如这样组织:
activity:存放各个页面Activityadapter:列表相关的Adapter(民宿列表、订单列表等)api:网络请求接口定义(Retrofit接口)entity:实体类,对应后端返回的JSON数据util:工具类(SP存储、日期转换等)
这里我用了Retrofit2 + OkHttp作为网络请求框架,Gson作为JSON解析工具。如果你们还没接触过Retrofit,用HttpURLConnection或Volley当然也行,但Retrofit的代码简洁度和维护性要明显高出一截,简历和论文里也更耐看。
网络层封装时注意两个点:
- 统一的拦截器,在请求头中加入token;
- 统一的异常处理,网络超时和连接失败要给出清晰提示,而不是让App崩溃。
OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(10, TimeUnit.SECONDS) .addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request original = chain.request(); String token = SpUtil.getInstance().getToken(); Request request = original.newBuilder() .header("token", token == null ? "" : token) .method(original.method(), original.body()) .build(); return chain.proceed(request); } }) .build();3.2 民宿列表页:卡片式列表与筛选条件
民宿列表是整个App最核心的界面。Android端用RecyclerView实现列表展示,每个item用CardView包裹,展示封面图、名称、城市、价格、可订状态。封面图用Glide加载,这部分是通用技能点。
“根据城市搜索”的功能实现也非常直观:搜索框输入城市关键字,调用接口GET /api/homestay/list?city=xx&page=1&pageSize=10,后端执行模糊查询后返回结果。价格区间筛选可以用RangeSlider(Material Components库),如果没有特殊UI要求,可以直接用两个EditText输入最低价和最高价,简单且不出错。
提示:Android端访问本机后端接口时,不能写
localhost或127.0.0.1,必须写电脑的局域网IP,例如http://192.168.1.101:8080。否则真机调试时永远连不上。这一点每年都有大量同学踩坑。模拟器里可以用10.0.2.2映射宿主机,但真机调试还是用局域网IP最稳。
3.3 民宿详情与预订下单流程
点击列表项进入详情页,展示多张轮播图、价格、设施列表、民宿介绍;下面放一个“立即预订”按钮,点击后弹出一个底部弹窗,选择入住日期和退房日期,系统自动算出入住天数(最少1晚)和总价,点击提交即创建订单。
订单金额计算逻辑放在后端比较安全,前端只做展示。后端在createOrder接口中根据民宿每晚单价和入住天数计算订单金额,同时要校验退房日期必须晚于入住日期,且所选时间段内房间剩余量充足。
这一段代码里有三个关键校验:
- 入住日期不能早于当前日期;
- 退房日期必须大于入住日期;
- 民宿状态必须为上架状态。
校验通过后,生成订单编号,格式推荐:yyyyMMddHHmmss + 4位随机数,保证唯一性。
3.4 Android上传图片到后端的实现(头像/房源图片)
我单独把这个功能拎出来,是因为很多同学做到一半会卡在这一步。民宿后台添加房源时需要上传封面图,用户修改头像也需要上传文件。
Android端选择图片用系统相册或相机,获取到图片的URI后转成File对象,通过OkHttp的MultipartBody上传到后端的文件上传接口。Java实现里这个逻辑不复杂,但有个大坑是不同手机的FileProvider配置,网上相关的解决方案很零碎。
后端接收文件的接口也很简单:
@PostMapping("/api/upload") public Result<String> upload(@RequestParam("file") MultipartFile file) { if (file.isEmpty()) { return Result.error(400, "文件为空"); } String originalFilename = file.getOriginalFilename(); String extName = originalFilename.substring(originalFilename.lastIndexOf(".")); String fileName = UUID.randomUUID().toString().replace("-", "") + extName; // 保存到指定目录 file.transferTo(new File(uploadPath, fileName)); return Result.success("/images/" + fileName); }图片上传后的访问路径是个容易疏忽的点。如果你的文件保存到项目本地磁盘,要配置静态资源映射,SpringBoot可以通过WebMvcConfigurer的addResourceHandlers方法将/images/**映射到本地上传目录,否则Android端拿到的URL根本加载不出图片。
3.5 订单状态流转与用户交互体验
订单状态是这个系统的业务命脉。我设计的流转规则是:待支付 → 已支付 → 已入住 → 已退房,中间状态可以取消(待支付时可取消,已支付后取消视为退订)。
Android端“我的订单”页面用TabLayout + ViewPager2切换不同状态,每个Tab对应一个订单状态码,列表展示订单摘要。这一部分把RecyclerView复用、多状态UI切换、下拉刷新都练习到了,是典型的Android综合实践场景。
值得一说的是接口的返回数据组织。为了让前端少点嵌套请求,后端在订单列表接口里可以一次性返回订单详情、民宿简要信息(名称、封面图)和民宿所在城市等字段,用VO类来组装,而不是让前端拿到订单后又继续请求民宿接口。写论文时,把“VO组装”这个点拿出来,能让你的系统设计比普通代码堆砌高一个档次。
4. 管理端:后台数据管理与权限控制
4.1 后台功能清单与设计思路
后台管理端我采用了一个轻量方案:复用Android端App,通过登录时识别角色标识(普通用户或管理员),动态显示不同的菜单入口。不用单独做Web后台,工作量会小很多,而且答辩时App上直接演示管理员功能也更直观。
后台功能清单:
- 房源管理:新增房源、编辑房源、上架/下架操作
- 订单管理:查看所有用户订单、修改订单状态(确认入住、确认退房)
- 用户管理:查看注册用户列表
如果时间充裕,可以再加一个“数据统计”页面,展示订单总量、营收总额、热门城市Top5,使用MPAndroidChart画柱状图或饼图,这个点很能提升项目的视觉完成度。
4.2 权限拦截的实现细节
后端拦截器对“管理员接口”需要双重校验:一是校验token是否存在且有效,二是校验token中携带的角色标识是否为管理员。Android端在普通用户登录状态下,即使手动拼接接口URL也访问不了管理员接口,这就是后端权限控制的必要之处。
拦截器里可以在JWT的Claims中加入role字段,然后在preHandle里读取并比对。如果只是简单地在后端接口里用if判断角色,会造成大量重复代码,不够优雅。
MyBatis-Plus的字段自动填充功能也建议用起来:创建时间、更新时间、逻辑删除这些公共字段统一处理,既能减少代码量,也能保证数据一致性。系统中所有表都建议统一增加create_time和update_time两个字段,答辩时讲数据审计观念,老师会认可。
5. 论文撰写、答辩演示与常见问题排查
5.1 毕业论文框架与各章节写作技巧
关于论文,我给你一套不容易出错的八章结构:
- 第一章 绪论:背景意义、国内外研究现状、论文组织结构
- 第二章 相关技术介绍:SpringBoot、Android、MySQL、MyBatis-Plus
- 第三章 系统分析:可行性分析、需求分析、用例图、业务流程
- 第四章 系统设计:总体架构图、功能模块设计、数据库设计(E-R图、表结构)
- 第五章 系统实现:各模块的核心代码与截图
- 第六章 系统测试:功能测试用例表格、测试结论
- 第七章 总结与展望
- 第八章(可选) 参考文献与致谢
写论文最大的误区是“堆需求描述”,罗列“用户可以注册、可以登录、可以下单”这类车轱辘话。老师真正想看到的是你做出的关键决策和理由,比如为什么用JWT而不是Session,为什么用MyBatis-Plus,为什么订单表要冗余民宿价格。每一个技术选型背后都有逻辑,把逻辑写清楚,论文的层次感立刻就不一样了。
5.2 答辩演示节奏与加分技巧
答辩时的演示顺序,我建议按照“用户端完整业务流 → 管理端后台操作”来安排,节奏别乱。
第一步,演示注册/登录,简单带过;第二步,进入首页浏览民宿,演示城市搜索和价格筛选;第三步,点击民宿详情,切换图片,选择入住日期并提交订单;第四步,演示订单状态从待支付到已支付的流转;第五步,切换管理员账号,演示订单状态更新和房源上下架。
整个过程控制在8到12分钟最合适。演示前务必备份数据,把演示用的测试数据准备好,不要临时用全新空库,否则列表一旦空白,场面会非常冷。另外,电脑要提前连接手机并开启USB调试,最好准备一张纸质的备用演示方案(录屏或截图),防止现场无法投屏。
答辩老师大概率会关注这几个问题:订单并发情况下如何防止超卖、密码如何存储、图片如何存储、Token过期如何处理。你只要能把这几个问题的解决方案讲清楚,效果就会很好,哪怕实现得不算完美,思路正确老师也会给分。
5.3 联调常见问题速查表
最后,我把自己和指导的学生在开发中经常遇到的坑整理成一个速查表,建议你收藏起来对照排查:
| 问题现象 | 常见原因 | 解决办法 |
|---|---|---|
| Android端访问后端超时 | 使用了localhost地址 | 改为电脑局域网IP |
| 中文乱码 | 数据库字符集或连接串未指派utf8mb4 | 建库用utf8mb4,url加characterEncoding=utf8mb4 |
| 接口返回时间是一串数字 | 未配置Jackson日期格式 | yml中配置jackson date-format和time-zone |
| 上传图片后URL无法访问 | 缺少静态资源映射 | 配置addResourceHandlers映射到上传目录 |
| 订单列表重复加载或数据错乱 | 没做分页或分页参数不对 | 统一使用page/pageSize,后端分页插件 |
| 真机安装后闪退 | 没有联网权限 | AndroidManifest中加INTERNET权限 |
| 注册时密码显示太长 | 密码字段长度过小 | 数据库字段设为varchar(128)以上 |
| Token失效后页面无响应 | 未做全局401处理 | Retrofit回调中判断code=401并跳转登录页 |
如果你准备用这套系统作为毕设,我的建议是:别一开始就想着把功能做得像携程一样齐全,先把主流程跑通,也就是“注册登录 + 浏览房源 + 下单支付 + 后台管理”,再把细节一点点补上。代码能跑通,论文有思路,答辩讲清楚,这三件事比什么都重要。
我个人在实际操作中的体会是,毕设最大的门槛往往不是某个技术难点,而是整个系统的串并联协调能力。前后端数据格式、字段命名、状态码是否统一,决定了你后期的联调效率。如果你现在正在为选题或实现发愁,不妨就按这个思路,把SpringBoot和Android两端拆开逐步攻克,最终你会发现自己完成的不仅仅是一个毕设项目,更是一套完整的全栈开发方法论。