做毕设最怕什么?不是写代码,而是折腾到最后一刻发现项目跑不起来、数据库没数据、演示的时候处处是坑。SpringBoot + Vue 房产销售系统这套完整项目,源码、SQL脚本、接口文档都齐了,就是冲着"拿来能用"去的。
这个项目适合谁?很简单:正在做 Java Web 毕设、需要前后端分离项目练手、或者想了解房产销售业务怎么落地的同学。技术栈不花哨,SpringBoot 3.x(或 2.x,看版本)配 Vue2/3 都行,MySQL 存储,JWT 做登录鉴权,前端管理后台加用户展示端,该有的模块都齐——房源管理、预约看房、订单签约、公告发布、统计报表。我把这个项目从头到尾拆一遍,从数据库设计到接口文档怎么组织,再到部署运行中的坑和答辩展示要点,一次讲清楚。
1. 项目定位与功能全景
1.1 核心需求解析
房产销售系统说白了就是"把线下的售楼处挪到线上"。客户在小程序或网页上看房、预约、留资,销售人员跟进客户、录入房源、管理签约。毕设级别不需要搞复杂的 ERP 逻辑,但两端的业务闭环必须完整:用户端能看能约,管理端能管能卖。
这套项目的定位非常明确:管理后台 + 用户前端。用户前端负责展示楼盘信息、房源列表、户型图、价格区间,注册登录后可以预约看房、收藏感兴趣的房源;管理后台则给不同角色使用——管理员管账号、管数据,销售人员管房源录入、处理预约、登记成交订单。
需要注意的是,这套系统本质上做的是"房源信息流 + 商机跟进流",不涉及真实的网签备案、贷款审批这些线下重流程。做毕设时在答辩中要能说清楚这个边界,这恰恰是项目的合理范围。
1.2 功能模块拆解
我把这套系统的核心功能模块整理了一下,各模块之间的关系如下:
- 用户认证模块:注册、登录、JWT 生成与校验、刷新令牌、权限路由。管理端和用户端走同一套认证体系,通过角色字段区分。
- 房源管理模块:房源信息的增删改查、上下架、图片上传、价格与户型字段维护。支持按区域、价格区间、户型、状态筛选。
- 预约看房模块:用户提交看房申请,销售人员查看预约列表、确认或驳回预约,附带联系时间、备注字段。
- 订单签约模块:预约确认后可创建订单,关联房源与客户,记录签约价格、付款方式、签约时间。
- 公告管理模块:管理员发布楼盘新闻、活动公告,用户端展示动态。
- 数据统计模块:房源总数、成交数量、成交总额、按销售员排行,通常用 ECharts 画柱状图和饼图。
- 客户管理模块:记录咨询客户信息,留资跟进,销售可以给自己的客户打标签、写跟进记录。
这些模块拆出来之后,开发顺序就有了:先做认证,再做房源 CRUD,然后做预约与订单,最后补统计和公告。每一块都是独立的表,代码也是独立的分层。
1.3 角色与权限设计
常见的角色划分是三种:系统管理员、销售顾问、普通用户(客户)。权限设计上不需要 Spring Security 的长篇配置,用 JWT 配合拦截器做路由级别校验就够:
| 角色 | 核心权限 | 典型操作 |
|---|---|---|
| 管理员 | 全部权限 | 账号管理、数据看板、房源上下架、公告管理 |
| 销售顾问 | 业务操作权限 | 房源录入与编辑、预约处理、订单登记、跟进客户 |
| 普通用户 | 用户端权限 | 浏览房源、预约看房、收藏房源、查看公告 |
这套角色设计在答辩中有个好处:可以顺着"谁在什么场景下需要什么功能"来讲,比单纯背代码有说服力。数据库层面就是 users 表里加一个 role 字段,路由守卫根据角色控制页面可见性。
2. 技术架构与选型逻辑
2.1 后端 SpringBoot 方案
SpringBoot 当家花旦的地位不用多说。内嵌 Tomcat、自动配置简化了大量 XML 配置,非常适合快速交付毕业设计。数据持久层用的是 MyBatis-Plus,它对单表 CRUD 几乎免写 SQL,分页插件直接传 pageNum、pageSize 就行。
反馈说毕设中一点要"有难度说明",SpringBoot 的 Web 层本身不复杂,真正的技术难点集中在三块:JWT 无状态鉴权的拦截流程、多表关联查询的 SQL 写法、图片上传与静态资源映射。这三块我会在后面详述。
依赖坐标大概这样(Maven):
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3</version> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>4.4.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency>2.2 前端 Vue 方案
前端用的是 Vue + Element UI + Axios + ECharts,组合稳得一批。Vue Router 做路由,Vuex 或 Pinia 管理用户登录态,Axios 统一封装请求拦截器把 token 加到请求头,响应拦截器统一处理 401 跳转登录。
管理端的页面结构大致是:登录页 → 布局页(左侧菜单 + 顶栏) → 各业务模块页面。用户端则是独立的页面:首页展示楼盘 banner、房源列表页带筛选条件、房源详情页、个人中心。
组件封装层面至少要抽出一个分页表格组件、一个图片上传组件、一个筛选表单组件。这样到写列表页和带条件的查询的时候就是拼积木,效率翻倍。
2.3 为什么选这套组合而不选别的
你可能要问,为什么不是 SpringCloud 微服务?不是 JSP + Servlet?不是前后端不分离?
核心原因:毕设考核的是完整度与业务逻辑,不是架构炫技。单体应用 + 前后端分离已经足够体现设计能力。微服务拆五个服务出来,光环境搭建就能耗掉半条命;JSP 那套早就跟不上主流开发模式,答辩时会被问倒;前后端不分离的项目演示起来也不够"现代"。SpringBoot + Vue 是当下 Java 开发岗位的主流技术栈,这套练熟了,简历上的项目经验是能写出东西的。
3. 数据库设计与 SQL 脚本构建
3.1 核心数据表结构
房产销售系统不需要几十张表,十张以内就是合理范围。核心表如下:
用户表 user
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键,自增 |
| username | varchar(50) | 登录账号 |
| password | varchar(100) | BCrypt 加密后的密码 |
| role | varchar(20) | admin / seller / customer |
| nickname | varchar(50) | 昵称 |
| phone | varchar(20) | 联系电话 |
| avatar | varchar(255) | 头像地址 |
| create_time | datetime | 创建时间 |
房源表 house
核心字段包括 title、cover、photos、area、price、layout(户型)、address、developer、status(0下架、1上架)、publish_time。注意 photos 建议用 JSON 数组存多个图片地址,或者用逗号分隔,不需要单独建一个图片表。
预约表 appointment
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| house_id | bigint | 关联房源 |
| user_id | bigint | 预约人 |
| appointment_time | datetime | 预约看房时间 |
| remark | varchar(255) | 备注 |
| status | int | 0待确认、1已确认、2已驳回 |
订单表 orders
订单表的命名要注意,order 是 MySQL 的关键字,建议用 orders 或者在 mybatis-plus 里指定 @TableName("orders")。字段包括 order_no(订单编号)、house_id、user_id、seller_id、total_price、payment_type、sign_date、status。
公告表 notice、客户跟进表 customer_follow根据需要精简即可。
3.2 表关系与关键 SQL 脚本
表之间的关系并不复杂:用户对房源是多对多(用户可浏览收藏多个房源,房源可被多个用户查看/预约),这里通过预约表和收藏表中转。订单是房源和用户的关联,同时加上售楼员字段。
SQL 脚本里需要注意几点:
- 建库指定 utf8mb4 字符集,防止中文乱码:
CREATE DATABASE IF NOT EXISTS house_sale DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;初始化账号要预置三个角色的账号各一个,并且密码用 BCrypt 加密后的字符串存储。很多同学直接存明文,答辩时被问到安全设计就卡壳,存加密串成本很低,印象分差距很大。
房源表预置 8-10 条真实感强的数据,价格、区域、户型要有差异。演示分页和筛选的时候才有素材。
多表查询是最容易出 bug 的地方,比如统计销售员成交总额:
SELECT u.nickname, COUNT(o.id) AS sale_count, IFNULL(SUM(o.total_price),0) AS total_amount FROM orders o LEFT JOIN user u ON o.seller_id = u.id WHERE o.status = 2 GROUP BY u.id ORDER BY total_amount DESC;3.3 SQL 脚本的使用场合
拿到项目里的 SQL 文件后,不要直接无脑执行。按流程来:
- 先建库,改掉默认密码和端口,避免和本地环境冲突。
- 按依赖顺序执行建表语句:user → house → appointment → orders → notice → 其他扩展表。
- 执行初始化数据脚本,确认账号能登录、房源数据和预约数据都在。
- 用 Navicat 或 DataGrip 跑几条测试 SQL,确认联表查询无异常。
这套项目好在 SQL 脚本是完整的,省掉了手工建表的步骤,但理解表之间的关系依然很重要——答辩时老师可能随手打开数据库让你解释这段关联关系,你得说得出来。
4. 接口文档与后端核心实现
4.1 RESTful 接口规范
接口文档的组织方式,按模块分组,每个接口标注方法、路径、请求参数、返回结构。规范统一用 RESTful,资源用名词复数,操作靠 HTTP 方法区分。典型的接口清单如下:
| 模块 | 方法 | 路径 | 说明 |
|---|---|---|---|
| 认证 | POST | /api/user/login | 登录返回 token |
| 认证 | POST | /api/user/register | 注册 |
| 房源 | GET | /api/house/page | 分页查询房源 |
| 房源 | GET | /api/house/{id} | 房源详情 |
| 房源 | POST | /api/house/save | 新增/编辑房源 |
| 房源 | PUT | /api/house/status/{id} | 上下架 |
| 预约 | POST | /api/appointment/add | 提交预约 |
| 预约 | GET | /api/appointment/list | 预约列表 |
| 订单 | POST | /api/orders/sign | 签约创建订单 |
| 统计 | GET | /api/statistics/overview | 数据看板 |
返回结构统一封装:
{ "code": 200, "msg": "操作成功", "data": {} }统一返回结构用泛型模式写,前端拿到 code 不为 200 时统一弹错误提示,拦截器里处理 401 跳转登录页。
4.2 JWT 登录鉴权的完整流程
鉴权设计是毕设答辩必考项之一。这里的流程可以这样实现:
- 用户提交账号密码到 /api/user/login。
- 后端从数据库查用户,用 BCrypt 校验密码密文。
- 校验通过后生成 JWT,payload 里放 userId、role、username,密钥写死或放在配置文件中,过期时间设 24 小时。
- 后续请求在 Header 带 Authorization: Bearer token。
- 后端自定义拦截器(HandlerInterceptor),在 preHandle 中解析 token。token 无效或过期直接返回 401,全程无状态。
Interceptor 注册时注意放行登录、注册、房源列表查询等无需鉴权的路径,其他路径全部拦。这一块的代码量不大,但能讲出"为什么用 JWT 而不是 Session"——这是因为前后端分离后,后端不维护会话状态,token 是适合跨域传递凭证的方式。
4.3 房源图片上传与静态资源映射
图片上传是毕设里高频功能。通常做法是:
- 前端用 Element 的 Upload 组件,把图片文件 POST 到 /api/upload。
- 后端接收 MultipartFile,保存到本地目录(比如项目根下的 upload/ 文件夹),文件名用 UUID 重命名防止冲突。
- 返回可访问的 URL,比如 /upload/xxx.jpg。
- 配置静态资源映射,把 /upload/** 映射到实际磁盘路径。
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { String path = System.getProperty("user.dir") + "/upload/"; registry.addResourceHandler("/upload/**") .addResourceLocations("file:" + path); }这个小功能融入房源管理模块后,整个项目"可用性"的观感直接提升一个档次。
4.4 接口文档的编写
接口文档是这套交付物里的加分项。常见方案有两种:一种是 Markdown 文档按模块手写;另一种是集成 Knife4j,启动项目后自动生成 Swagger 文档。
建议时间充裕就把 Knife4j 加上,Controller 上补几个 @Schema 注解,答辩直接打开浏览器展示在线接口列表,专业感拉满。手写接口文档也不能丢,至少要把请求参数、返回示例写清楚,方便项目交接时别人接手。
5. 实操部署与踩坑记录
5.1 本地运行步骤
理论讲再多不如自己跑一遍。这个项目本地运行的顺序是:
- 准备环境:JDK 8 或 11、Maven 3.6+、MySQL 5.7 或 8.0、Node 14+。
- 导入数据库:用 Navicat 执行项目里的 SQL 脚本,确认库名和账号密码。
- 后端配置修改:在 application.yml 里改数据库地址、账号、密码。端口默认 8080,要不起冲突就改 8081。
- 启动后端:开发环境直接 run Application.java,看到 "Started Application in x.xx seconds" 即启动成功。
- 前端依赖安装:
npm install,如果网络慢可以用国内镜像源加速。 - 启动前端:
npm run serve。默认端口 8080 和 8081 只要不同就万事大吉。 - 浏览器访问前端地址,用预置的管理员账号登录管理后台。
部署 demo 的时候我习惯把后端端口改成 8081,前端 Vue 的 devServer 配代理转发到 8081,避开端口冲突的同时也解决跨域问题。
// vue.config.js module.exports = { devServer: { port: 8080, proxy: { '/api': { target: 'http://localhost:8081', changeOrigin: true } } } }5.2 我实际踩过的坑
数据库连接乱码。项目里 SQL 脚本是 utf8mb4,但如果 MySQL 连接串忘了加characterEncoding=utf8,查询出来就有中文乱码。解决办法是给连接 URL 加上:
jdbc:mysql://localhost:3306/house_sale?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/ShanghaiMyBatis-Plus 逻辑删除没配导致数据删除报错。如果表里加了 deleted 字段,没在实体类上配 @TableLogic,Mapper 的 delete 操作会变成真正的物理删除。初始化项目跑起来后发现数据没了,大概率是这个原因。
前端路由刷新 404。原因是前端路由是 history 模式,生产环境部署到 Nginx 后需要配置 try_files,否则刷新页面就白屏。本地开发没有这个问题,部署到服务器上才会遇到。
JWT 拦截器把登录接口拦截了。这个很微妙,因为你明明把登录路径放行了,但如果路径写的是 /api/user/login,而拦截器注册时匹配的是 /api/**,那就会误判。必须仔细检查排除路径,建议用excludePathPatterns("/api/user/login", "/api/user/register", "/upload/**")。
分页查询传 pageNum 和 pageSize 但返回 null。遇到这种问题,先确认 MyBatis-Plus 分页拦截器有没有注册,漏配了分页插件就只是全量查询,前端拿到的是整张表还分不了页。
5.3 常见问题速查表
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 启动报 8080 端口占用 | Tomcat 端口冲突 | 后端 application.yml 改端口 / 查 netstat |
| 前端请求接口 404 | 代理未配置或接口路径错误 | 看 Network 面板请求的实际 URL |
| 登录一直失败 | 密码加密方式不匹配 | 确认前端没有二次加密、确认数据库密码密文是否有误 |
| 图片上传后访问 404 | 静态资源映射没生效 | 检查后端的 addResourceHandlers 和实际路径 |
| 打包后运行提示缺少主类 | 打包插件配置不对 | 确认 maven 的 spring-boot-maven-plugin 已配置 |
6. 答辩演示与二次扩展建议
6.1 演示顺序设计
答辩演示时间一般只有 5-10 分钟,顺序很关键:
- 1 分钟讲项目背景和毕业设计的选题意义,一句话一带而过即可。
- 2 分钟按功能模块逐个演示:登录三种角色 → 管理员看看板 → 添加房源 → 用户注册登录预约 → 销售后台处理预约 → 登记订单。
- 2 分钟打开数据库展示表关系和 SQL 脚本,挑一个多表查询现场跑一遍 SELECT,边跑边解释语句含义。
- 1 分钟展示接口文档或在线 Swagger,强调 RESTful 规范。
演示过程中千万不要花时间去敲代码,老师想看的是项目是一个整体、能跑、你能讲清楚每一块。
6.2 二次扩展建议
如果想让这个项目在答辩中更有竞争力,可以低成本做一些增量:
- 看房预约的短信通知——这里接入一个模拟的短信服务即可,按日志输出通知内容。
- 成交趋势图——统计模块里加一个按月成交额折线图,前端 ECharts 代码量很小,但视觉效果好。
- 房源地图展示——用高德地图开放平台把房源坐标标记到地图上,前后端加起来一天能搞定。
- 导入导出功能——房源数据 Excel 导入导出,用 EasyExcel 依赖,也是面试官喜欢的点。
- WebSocket 消息通知——预约被确认时,用户端实时收到提醒,Swagger 里再加个 websocket endpoint,这个扩展技术含量直接上一个段位。
6.3 写在最后的实践心得
带过不少项目,每次给毕设项目做技术选型,我始终坚持一条:项目是给人用的,不是给代码看的。数据库有没有初始化数据、演示的时候有没有预先准备好的账号、接口文档是否齐全,这些看起来不起眼,实际决定了这个项目是"能答辩的作品"还是"跑不动的代码堆"。
做这套房产销售系统,最难的不是某一个功能本身,而是把登录、房源、预约、订单、统计这样一个完整链路串起来跑通。源码在手不等于万事大吉,你自己过一遍核心流程,把这些逻辑真正转化成你自己的表达——到时候不论老师问 JWT 为什么不用 Session,还是问订单表为什么这样设计,你都能从业务和工程两个层面给出答案。这才是这份源码给你带来的真正价值。