
公寓租赁系统这个题目在计算机毕设里属于那种看起来平平无奇、但实际做起来非常能体现综合能力的选题。它不像“基于深度学习的图像识别”那样听起来高大上也不像“网上商城系统”那样已经被做烂了而是恰好卡在“业务逻辑有一定复杂度”和“开发量适中”的黄金位置。后台管理要管房源、管合同、管账单前台要处理注册登录、预约看房、在线签约中间还夹着权限控制和状态流转一套完整做下来SpringBoot、Vue、MySQL、MyBatis-Plus这些毕设高频技术栈全都能覆盖到。这篇博文我会把这套系统的设计思路、核心表结构、关键代码实现、前后端联调以及部署上线的坑全部拆开讲直接给出一套可落地的方案。适合正在纠结毕设选题、或者已经选了公寓租赁系统但不知道从哪下手的同学参考。1. 选题价值与需求拆解1.1 为什么公寓租赁系统是毕设“安全牌”先说个实在的观点毕设选题的第一原则不是“炫技”而是“可控”。所谓可控指的是三个月之内你能独立完成同时工作量足够通过答辩并且每一块功能都能讲清楚“为什么这么做”。公寓租赁系统就非常符合这个标准。它的业务场景很直观——房东发布房源、租客浏览找房、在线预约看房、签订电子合同、按期缴纳房租管理员在后台做全局管理。这套流程人人都住过房子、都懂业务不需要额外学习领域知识所以你能把主要精力全部放在技术实现上。更重要的是这个题目包含了两条明确的主线一条是面向租客和房东的前台业务系统另一条是面向管理员的运营后台。两条线共用一套用户体系和房源数据这就天然要求你做权限设计、状态机设计、接口隔离这些能力恰恰是答辩时老师最关注的点。相比之下那种只有一个后台CRUD的管理系统或者是纯展示类的网站工作量一张嘴就能说完毫无竞争优势。1.2 功能需求拆解从业务场景到模块划分很多同学拿到题目之后的第一反应是直接建表写代码这是典型的错误顺序。正确做法是先画业务流程图把角色、动作、数据状态理清楚再开始设计。这套系统里一共涉及三种角色管理员、房东、租客。管理员负责审核房源、管理用户、查看平台运营数据房东负责发布房源、处理预约、维护房源状态租客负责搜索房源、发起预约、在线签约缴费。围绕这三个角色核心模块可以拆成以下几块用户模块注册、登录、个人信息维护、密码修改、角色权限控制房源模块房源发布、房源列表、条件筛选、房源详情、上下架管理预约模块租客发起看房预约、房东处理预约、预约状态流转合同模块在线生成租赁合同、合同签署、合同查询账单模块按照合同周期生成账单、在线缴费、缴费记录查询后台管理模块用户管理、房源审核、数据统计这里要特别强调一下房源审核。很多毕设项目忽略这个环节房源一发出来就直接上架展示这在业务上是不合理的。真实场景下管理员必须对房东发布的房源进行审核防止虚假房源这既增加了系统的完整度也让你在论文里多了一个状态流转的论述点一举两得。2. 技术选型与架构设计2.1 技术栈选型SpringBoot Vue 为什么是主流打开任何一个毕设选题网站的排行榜SpringBoot加Vue的组合能占到一半以上这不是偶然。SpringBoot极大简化了Java后端项目的配置内嵌Tomcat一个jar包直接跑起来省掉了传统SSH框架里一大堆XML配置Vue作为前端框架组件化开发让页面维护变得简单配合Element UI组件库几天时间就能把管理后台的界面搭得漂漂亮亮。具体到这套公寓租赁系统后端我建议用SpringBoot 2.7加MyBatis-Plus。MyBatis-Plus是我个人非常推荐的一个持久层框架它把单表的增删改查都封装好了你只需要写业务逻辑不需要手写基础SQL。配合代码生成器建完表之后自动生成实体类、Mapper接口、Service层能省下大量时间。加一个条件构造器QueryWrapper稍微复杂一点的动态条件查询也能轻松搞定完全满足毕设场景。前端选择Vue 2加Element UI还是Vue 3加Element Plus取决于你对哪个更熟。Vue 3的响应式原理用Proxy实现了更好的性能但Vue 2的生态更成熟、踩坑资料更多。我个人的建议是如果你之前用过Vue 2就继续用Vue 2稳定压倒一切如果你是从零开始学直接上Vue 3加Element Plus毕竟现在新项目的主流方向已经全面转向Vue 3了。2.2 前后端分离架构与项目结构系统整体采用前后端分离架构前端单独起一个Vue工程通过HTTP接口与后端通信后端只提供数据接口不关心页面渲染。这个架构的核心优势在于职责分离前端只管页面展示和用户交互后端只管数据处理和业务逻辑两边可以并行开发联调阶段再对接。后端项目的包结构推荐这样组织com.example.apartment ├── controller // 接口层接收前端请求 ├── service // 业务层处理具体业务逻辑 │ └── impl // 业务实现类 ├── mapper // 数据访问层MyBatis-Plus的Mapper接口 ├── entity // 实体类对应数据库表结构 ├── dto // 数据传输对象接收前端参数 ├── vo // 视图对象返回给前端的数据 ├── config // 配置类如拦截器、跨域配置 ├── common // 公共类如统一返回结果、异常处理 └── utils // 工具类如JWT工具这个分层方式对应了我当年总结的一条经验答辩时老师最喜欢问的就是“你这个项目是怎么分层的”你能把每一层的职责说清楚就已经赢了一半了。3. 数据库设计与核心表结构3.1 核心表结构设计数据库设计是整套系统的地基表一旦建得不合理后面写代码处处别扭。公寓租赁系统核心的表我梳理下来一共需要六张用户表、房源表、预约表、合同表、账单表、通知公告表。下面把最关键的三张表拆开讲。用户表sys_user要注意的点是角色字段的处理。我建议直接用一个role字段存储角色编码比如0表示管理员、1表示房东、2表示租客。有些项目为了显得专业会建角色表和用户角色关联表但毕设场景下业务量不大多表关联反而增加了复杂度用简单字段区分完全够用。房源表house是整个系统里字段最多的表设计时需要覆盖两类信息一类是基础属性比如标题、描述、户型、面积、租金、地址另一类是状态字段比如审核状态0待审核、1已通过、2已驳回、出租状态0未出租、1已出租。这里有个设计细节容易被忽略——经纬度字段。如果你希望系统能按地图定位展示房源位置提前设计两个字段存经度和纬度会省掉后面改表的麻烦。合同表contract的核心是关联关系。合同既要关联房源也要关联租客和房东三个外键缺一不可。合同编号建议用“HT加日期加随机数”的规则生成比如HT20250115001。出租开始时间和结束时间用于后续账单周期计算这在账单模块会频繁用到索引一定要加上。3.2 表关系与权限模型设计六张表之间的关系可以这样理解用户在房源表中作为房东发布房源房源表中有一个user_id字段指向user表的主键表示“这套房源是谁发布的”。租客在预约表中发起看房预约预约表同时关联user_id和house_id。合同签订后合同表同时关联房源、租客、房东三方。账单表的contract_id关联合同表通过合同里的租期和租金计算出账单金额。权限模型的落地方式是拦截器加JWT。用户登录成功后后端生成一个包含用户ID和角色的JWT令牌返回给前端前端在后续请求的请求头里带上这个令牌。后端写一个拦截器统一拦截请求先从令牌中解析出用户信息再根据接口需要的角色做校验。例如房源审核接口代码里先判断当前用户角色是否为管理员不是直接返回无权限提示。4. 核心功能实现细节4.1 用户登录与JWT鉴权实现用户登录接口的逻辑并不复杂但有几个细节直接影响安全性和体验。密码必须用BCrypt加密存储不能明文保存。登录校验通过后生成JWT令牌返回令牌里只放userId和role这两个必要信息过期时间设置为24小时。前端拿到令牌后存储在LocalStorage里每次请求在Axios拦截器中自动携带。// 登录接口核心逻辑 public Result login(LoginDTO loginDTO) { // 1. 根据用户名查询用户 LambdaQueryWrapperSysUser wrapper new LambdaQueryWrapper(); wrapper.eq(SysUser::getUsername, loginDTO.getUsername()); SysUser user userMapper.selectOne(wrapper); // 2. 校验密码是否匹配 if (user null || !BCrypt.checkpw(loginDTO.getPassword(), user.getPassword())) { return Result.error(用户名或密码错误); } // 3. 生成JWT令牌并返回 String token JwtUtil.generateToken(user.getId(), user.getRole()); MapString, Object data new HashMap(); data.put(token, token); data.put(role, user.getRole()); return Result.success(data); }4.2 房源管理与条件查询房源列表是租客端使用频率最高的接口它的核心难点是动态条件查询。用户可能按区域筛选、按价格区间筛选、按户型筛选也可能什么都填直接搜索。如果用传统的写法需要手动拼接SQL判断条件是否为空代码会非常冗余。MyBatis-Plus的LambdaQueryWrapper能优雅解决这个问题。public PageHouseVO queryHouseList(HouseQueryDTO queryDTO) { PageHouse page new Page(queryDTO.getPageNum(), queryDTO.getPageSize()); LambdaQueryWrapperHouse wrapper new LambdaQueryWrapper(); // 动态拼接查询条件条件为空时自动跳过 wrapper.eq(StringUtils.hasText(queryDTO.getCity()), House::getCity, queryDTO.getCity()) .eq(queryDTO.getHouseType() ! null, House::getHouseType, queryDTO.getHouseType()) .between(queryDTO.getMinPrice() ! null queryDTO.getMaxPrice() ! null, House::getRent, queryDTO.getMinPrice(), queryDTO.getMaxPrice()) .eq(House::getAuditStatus, 1) // 只展示审核通过的房源 .eq(House::getRentStatus, 0) // 只展示未出租的房源 .orderByDesc(House::getCreateTime); return houseMapper.selectPage(page, wrapper); }房源发布接口要注意的是发布的房源必须有一个初始状态。我设计的初始状态是审核状态为待审核、出租状态为未出租。这样管理员在后台看到的是待审核列表房东在自己的房源列表中能看到审核进度租客在前台永远看不到未审核的房源三层隔离非常清晰。4.3 预约看房与合同管理预约看房的业务逻辑包含一个典型的“防重复”校验。同一个租客对同一套房源在预约处于待处理状态时不能重复提交预约。这里的实现方式是在预约表中添加一个唯一约束或者查询时同时校验租客ID、房源ID和预约状态。房东处理预约时同意则预约状态改为已通过拒绝则改为已拒绝并填写驳回原因。这里有个交互上的建议拒绝时允许填原因前端在租客端以醒目颜色展示拒绝原因这虽然只是一个很小的功能点但在答辩演示时能给老师留下“考虑到了细节”的印象。合同模块的亮点在于租金计算。合同签订后系统根据开始时间和结束时间计算总月份数乘以月租金得到应付总额。这里有个精度问题要提前处理Java里计算金额不能直接用double类型浮点数计算会丢失精度必须使用BigDecimal。别在这上面栽跟头我的同学踩过这个坑房租算出来多了几毛钱排查了半天才发现是数据类型的问题。4.4 账单生成与缴费记录账单生成模块其实是一个定时任务加一个触发规则。定时任务可以每天跑一次查询所有生效中的合同判断当前日期是否到了缴费日到了就自动生成账单。这种实现方式在毕设里已经相当亮眼。// 定时任务每天检查并生成账单每天凌晨2点执行 Scheduled(cron 0 0 2 * * ?) public void generateBills() { // 1. 查询所有状态为生效中的合同 ListContract contracts contractMapper.selectList( new LambdaQueryWrapperContract().eq(Contract::getStatus, 1)); // 2. 遍历合同根据缴费周期生成账单 for (Contract contract : contracts) { Date now new Date(); // 如果当前日期是缴费日且该周期还没生成过账单则生成 if (shouldGenerateBill(contract, now)) { Bill bill new Bill(); bill.setContractId(contract.getId()); bill.setAmount(contract.getMonthlyRent()); bill.setHouseId(contract.getHouseId()); bill.setStatus(0); // 0待缴费 1已缴费 billMapper.insert(bill); } } }5. 前端页面与接口联调5.1 前端工程结构与路由设计前端部分需要用到Vue Router做页面路由管理、Vuex或Pinia做全局状态管理、Axios做HTTP请求。页面大致分为两个部分租客端H5风格页面和管理后台PC端页面。租客端的页面包括首页、房源列表页、房源详情页、预约记录页、个人中心页。管理后台包含用户管理页、房源审核页、数据统计页、账单管理页。路由守卫一定要做前端路由需要根据角色判断是否能访问比如未登录用户访问个人中心要自动跳转到登录页。5.2 接口联调与Axios封装前后端分离开发最大的痛点是联调阶段的接口不一致问题。我建议前端拿到后端接口文档后先统一封装一个request工具类把所有请求统一走拦截器。// Axios请求封装 import axios from axios import { MessageBox } from element-ui const request axios.create({ baseURL: /api, // 开发环境通过代理转发到后端端口 timeout: 10000 }) // 请求拦截器自动携带token request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config }) // 响应拦截器统一处理错误码 request.interceptors.response.use( response { const res response.data if (res.code ! 200) { MessageBox.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) } return res }, error { if (error.response error.response.status 401) { MessageBox.error(登录状态已过期请重新登录) localStorage.removeItem(token) window.location.href /login } else { MessageBox.error(网络异常请稍后重试) } return Promise.reject(error) } ) export default request开发环境的跨域问题通过Vue CLI的代理配置解决在vue.config.js中加一行devServer配置即可。这里有一个最常见的坑就是后端接口地址拼接了context-path导致代理转发404。解决方案是后端统一不加context-path或者前端代理时配置对应的pathRewrite让代理路径能正确映射。6. 部署上线与本地环境搭建6.1 本地环境搭建三步走很多同学写毕设卡在了第一步——环境搭建。三步走方案如下第一步安装JDK 1.8以上、Maven 3.6以上、MySQL 5.7以上、Node.js 14以上四个基础环境第二步创建数据库导入项目里的SQL脚本修改后端配置文件的数据库账号密码第三步以后端和前端分别启动。后端启动时有一个高概率报错是数据库连接失败比如Access denied for user。这个报错的原因99%是配置文件没改对常见问题包括密码错误、数据库名不对、端口不是默认的3306。启动前打开application.yml把数据库名、用户名、密码逐项核对一遍能省下大量排查时间。前端启动则要先执行npm install安装依赖这一步在网络状况不好时可能卡住解决方法是切换到淘宝镜像源安装速度会明显提升。# 后端启动命令 mvn clean package -DskipTests java -jar target/apartment-system.jar # 前端启动命令 npm install # 第一次需要安装依赖 npm run serve # 开发模式启动6.2 部署上线本地跑通之后如果你想直接把项目部署到远程服务器可以在服务器上装一个宝塔面板使用Docker或直接安装环境。打包时后端执行mvn package命令生成可执行的jar包前端执行npm run build生成dist静态文件然后用Nginx把静态文件和接口请求统一代理到后端端口。这里要重点说下Nginx配置。前端部署在80端口或443后端接口监听在8080端口Nginx中配置一个反向代理把/api路径的请求转发给8080。这个思路和本地开发时的代理一致所以只要本地配置调试好部署到服务器上也基本不会出问题。6.3 常见问题与排查技巧实录我在实操过程中整理了一些高频问题每个都是踩过的坑直接列成速查表供你参考问题现象大概率原因解决方案前端请求接口报404代理路径没匹配上检查vue.config.js的proxy配置确认pathRewrite登录后请求接口返回401Token过期或缺失检查前端拦截器是否携带token确认token过期时间数据库中文乱码连接字符集不是UTF-8连接地址加参数characterEncodingutf8表结构检查字符集跨域报CORS错误后端未开启跨域后端添加CorsFilter配置类允许跨域代码正常但接口响应慢SQL走了全表扫描给常用查询字段加索引比如house表的audit_status和rent_status排查问题有个口诀先看后端日志再看前端控制台最后看网络请求。后端日志报什么错直接决定排查方向前端控制台能暴露运行时的JavaScript错误网络请求面板能查看具体的请求参数和返回结果。大多数问题顺着这条链路走到一半就能定位到原因了。6.4 答辩提问预测与应对思路最后聊一下答辩环节。老师问的问题通常不会离开这几个方向系统功能怎么设计的、数据库为什么这么设计、某个功能是怎么实现的、你有什么收获。功能设计方面把三种角色的业务链路讲清楚再讲一下审核状态流转和预约状态流转就能展现出思考深度。数据库设计方面讲清楚为什么用逻辑删除而不是物理删除、为什么某些字段要加索引这是在体现基础功。技术实现方面把JWT鉴权的原理、MyBatis-Plus条件构造器的用法、定时任务的触发机制讲明白这是在体现你对用过的技术有真实理解。收获方面讲一个具体的坑比如金额计算用了double导致精度丢失最后用BigDecimal解决这种故事比任何空话都有说服力。做这套系统的实际体会是毕设最忌贪大求全。与其设计十个功能每个都做得粗糙不如把核心链路打磨精细。我见过太多同学一开始规划了十几个模块最后做不完草草收尾。公寓租赁系统这套选题的优势就在于核心模块明确需求边界清晰你只要把用户、房源、预约、合同、账单这五条线打通再把审核、状态流转、权限控制这几个点做扎实就已经达到了一篇合格甚至优秀毕设的标准。剩下的时间多写写注释、整理整理数据库设计说明比盲目堆功能有价值得多。