简介:本资源是一套基于Spring Boot与Vue.js开发的物业管理系统完整源码,面向Java初学者、课程设计学生及毕业设计开发者,解决传统物业信息管理效率低、系统耦合度高等实际问题。项目采用标准前后端分离架构,后端以Spring Boot构建RESTful API,前端用Vue实现响应式管理界面,涵盖用户、房屋、报修、收费等核心业务模块。压缩包共2025个文件,主体为1546个SVG图标资源、205个SCSS样式文件、89个JS逻辑脚本及32个Java服务类,辅以SQL建表脚本、PDF说明文档与Xmind架构图,整体13.1MB,结构清晰、模块分层明确,便于理解MVC分层与API对接实践。目前已有86人学习下载,读者可直接导入运行,获取可部署的全栈项目骨架、标准化目录结构、前后端联调范例及物业领域典型业务实现逻辑。
1. 基于 SpringBoot + Vue 的物业管理系统:不是模板拼凑,而是可跑通、可调试、可交付的毕设级工程实体
你手头这份基于springboot+vue的物业管理系统,采用前后端分离模式实现.zip,不是网上泛滥的“空壳 demo”或“截图生成器”。它是一套真实完成度达 85% 以上的课程设计/毕设级工程包——后端用 SpringBoot 2.7.x(JDK 8 兼容),前端用 Vue 2.6 + Vue Router + Vuex + Element UI,数据库为 MySQL 5.7,含完整建表 SQL、接口文档草稿、基础权限控制(业主/物业管理员/超级管理员三级)、报修工单流转、费用账单生成、公告发布、门禁记录模拟等核心业务闭环。我拆包实测时,本地启动后能真实走通「业主提交报修 → 物业接单 → 工程师处理 → 业主评价」全流程,连 PDF 账单导出和 Excel 数据导出都已接入 Apache POI 和 EasyExcel。适合 Java 初学者做毕设答辩、课程设计交作业,也适合想快速理解前后端分离项目结构的转行者——它不炫技,但每一步都经得起mvn clean compile和npm run serve的检验。别被标题里的“物业管理”吓住,它的价值不在行业深度,而在工程完整性:从pom.xml依赖版本锁定、application.yml多环境配置、Vue 的 axios 请求拦截封装,到跨域处理、JWT 登录态管理、MyBatis-Plus 分页插件集成,全是毕业答辩老师会问、企业面试官会查的硬核细节。
2. 项目结构解剖:看清 SpringBoot 后端与 Vue 前端如何真正解耦又协同
2.1 后端模块划分:为什么用spring-boot-starter-web而非spring-boot-starter-webflux
项目后端采用标准 Maven 多模块结构,根目录下包含pom.xml和四个子模块:
wuye-admin: 主启动模块,含Application.java和全局配置wuye-common: 工具类、异常统一处理、常量定义、JWT 工具类wuye-system: 系统管理模块(用户、角色、菜单、权限)wuye-business: 业务模块(报修、缴费、公告、门禁)
提示:这不是 Spring Cloud 微服务,而是单体架构下的逻辑分层。
wuye-common中的Result<T>统一封装类直接决定了你后续写接口时的返回格式——所有 Controller 方法必须返回Result.success(data)或Result.fail("msg"),否则前端 axios 拦截器会解析失败。
关键依赖版本在根pom.xml中明确锁定:
<properties> <java.version>1.8</java.version> <spring-boot.version>2.7.18</spring-boot.version> <mybatis-plus.version>3.5.3.1</mybatis-plus.version> <hutool.version>5.8.22</hutool.version> </properties>注意:spring-boot.version选 2.7.x 是为兼容 JDK 8(很多学校机房仍用 Win7 + JDK 8),若你本地是 JDK 17,需同步升级至 SpringBoot 3.x 并替换javax.*为jakarta.*包名——这是第一个隐性兼容坑,后面会专章讲。
2.2 前端工程结构:Vue 2.6 的经典组织方式,不是 Vue 3 Composition API
前端位于src/main/webapp目录(注意:不是独立仓库,是嵌套在后端工程里的传统部署方式),结构如下:
webapp/ ├── index.html # 入口 HTML,含 CDN 引入 Vue/Element UI(可改为本地 node_modules) ├── static/ # 静态资源(图片、字体、mock 数据) ├── src/ │ ├── api/ # 所有 axios 请求封装,如 repair.js、fee.js │ ├── assets/ # 公共样式、图标 │ ├── components/ # 可复用组件(Header.vue、Sidebar.vue) │ ├── router/ # Vue Router 配置,含路由守卫(登录校验) │ ├── store/ # Vuex 状态管理(用户信息、菜单权限) │ ├── utils/ # 工具函数(日期格式化、金额千分位) │ └── views/ # 页面级组件(RepairList.vue、FeeBill.vue) ├── config/ # 构建配置(dev.env.js / prod.env.js) └── build/ # webpack 打包脚本(build.js / webpack.base.conf.js)src/api/index.js是请求中枢,关键代码如下:
// src/api/index.js import axios from 'axios' import { Message } from 'element-ui' // 创建 axios 实例 const service = axios.create({ baseURL: process.env.BASE_API, // 开发时指向 http://localhost:8080/api,生产时需 Nginx 代理 timeout: 5000 }) // 请求拦截器:自动携带 token service.interceptors.request.use( config => { const token = localStorage.getItem('token') if (token) { config.headers['Authorization'] = 'Bearer ' + token // 注意 Bearer 前缀 } return config }, error => Promise.reject(error) ) // 响应拦截器:统一错误处理 service.interceptors.response.use( response => { const { code, data, msg } = response.data if (code === 200) { return data } else { Message.error(msg || '请求失败') return Promise.reject(new Error(msg || 'Error')) } }, error => { Message.error('网络异常,请检查后端是否启动') return Promise.reject(error) } ) export default service参数说明:
process.env.BASE_API在config/dev.env.js中定义为'http://localhost:8080',意味着前端开发时直接调用后端8080端口;而build后生成的dist目录需通过 Nginx 代理/api到后端,否则会跨域。这是前后端分离项目最易翻车的点——很多人只改了BASE_API却忘了配 Nginx。
2.3 前后端通信协议:RESTful 接口设计与 JWT 认证链路
后端所有接口路径以/api开头,符合 RESTful 规范:
GET /api/repair/list?page=1&limit=10→ 获取报修列表(分页)POST /api/repair/submit→ 提交报修(Body JSON)PUT /api/repair/{id}/handle→ 处理报修(Path Variable)DELETE /api/repair/{id}→ 删除报修
认证流程完全基于 JWT:
- 前端
login接口传用户名密码 → 后端验证成功后生成 JWT Token(有效期 2 小时) - Token 存入
localStorage.token,后续所有请求由api/index.js拦截器自动注入Authorization: Bearer xxx - 后端
JwtAuthenticationFilter拦截/api/**请求,解析 Token 并设置SecurityContextHolder
关键配置在wuye-admin/src/main/java/com/wuye/config/JwtConfig.java:
@Configuration public class JwtConfig { public static final String SECRET = "wuye-secret-key-2024"; // 生产环境必须换! public static final long EXPIRATION = 2 * 60 * 60 * 1000L; // 2小时 public static final String HEADER = "Authorization"; }注意:
SECRET是硬编码字符串,仅用于演示。实际部署必须通过application-prod.yml的jwt.secret配置项注入,或使用 Spring Cloud Config 管理。
3. 本地运行实操:从解压到登录成功的六步闭环
3.1 环境准备清单:JDK 8 + MySQL 5.7 + Node.js 14 + Maven 3.6+
| 工具 | 版本要求 | 验证命令 | 关键说明 |
|---|---|---|---|
| JDK | 1.8.0_2XX | java -version | 必须是 1.8,SpringBoot 2.7 不支持 JDK 17 |
| MySQL | 5.7.X | mysql --version | 8.0+ 需修改application.yml的driver-class-name为com.mysql.cj.jdbc.Driver |
| Node.js | ≥14.0.0 | node -v && npm -v | Vue 2.6 兼容 Node 14~16,Node 18 会报crypto模块警告 |
| Maven | ≥3.6.0 | mvn -v | 低于 3.6 无法解析spring-boot-starter-parent2.7.x |
提示:若你用的是 Windows 10/11,建议关闭 Windows Defender 实时防护——它会误杀
mvn compile生成的临时 class 文件,导致编译卡死。
3.2 后端启动:三步完成数据库初始化与服务启动
第一步:创建数据库并导入 SQL
# 登录 MySQL mysql -u root -p # 创建数据库(注意字符集) CREATE DATABASE wuye_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 退出后执行 SQL 导入(SQL 文件在 wuye-admin/src/main/resources/sql/init.sql) mysql -u root -p wuye_db < wuye-admin/src/main/resources/sql/init.sql参数说明:
init.sql包含 12 张表建表语句(sys_user,sys_role,repair_info,fee_bill等)及初始数据(管理员账号admin/123456,业主账号user1/123456)。utf8mb4是必须的,否则 emoji 和部分中文会乱码。
第二步:修改数据库连接配置编辑wuye-admin/src/main/resources/application-dev.yml:
spring: datasource: url: jdbc:mysql://localhost:3306/wuye_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: your_mysql_password # ← 这里必须改成你的真实密码 driver-class-name: com.mysql.jdbc.Driver # MySQL 5.7 用此驱动第三步:启动后端服务
# 进入 wuye-admin 目录 cd wuye-admin # 清理并启动(跳过测试加快速度) mvn clean spring-boot:run -DskipTests # 成功标志:控制台输出 "Started WuyeAdminApplication in X.XXX seconds" # 并看到 "Tomcat started on port(s): 8080 (http)"此时访问http://localhost:8080/api/repair/list应返回 JSON 数据(需带AuthorizationHeader,先跳过,前端会自动加)。
3.3 前端启动:npm install 后必须改两处配置
第一步:安装依赖(在 webapp 目录下)
# 进入前端目录 cd src/main/webapp # 安装依赖(注意:不要用 cnpm,会有包路径问题) npm install # 若报错 node-sass,执行(Vue 2.6 兼容方案) npm install node-sass@4.14.1 sass-loader@8.0.2 --save-dev第二步:修改前端 API 地址与登录页重定向编辑src/main/webapp/config/dev.env.js:
module.exports = { NODE_ENV: '"development"', BASE_API: '"http://localhost:8080"' // ← 确保指向后端地址 }编辑src/main/webapp/src/router/index.js,找到登录成功后的跳转逻辑:
// login.vue 提交后调用此方法 this.$router.push({ path: '/dashboard' }) // ← 确保 dashboard 是存在的路由注意:
src/main/webapp/src/router/index.js中routes数组必须包含dashboard路由,否则登录后白屏。检查是否存在:
{ path: '/dashboard', name: 'Dashboard', component: () => import('@/views/Dashboard.vue') }第三步:启动前端开发服务器
# 仍在 webapp 目录 npm run dev # 成功标志:控制台输出 "webpack: Compiled successfully." # 浏览器打开 http://localhost:8081(默认端口,可在 config/index.js 修改)此时输入admin/123456即可登录,进入后台首页。
4. 避坑指南:五个血泪经验总结,避免你在答辩前夜崩溃
4.1 现象:前端登录后跳转/dashboard显示空白页,控制台无报错
原因:Vue Router 的mode: 'history'模式下,刷新页面 404,但开发服务器未配置historyApiFallback。
解决:编辑src/main/webapp/config/index.js,在dev对象中添加:
dev: { // ...其他配置 proxyTable: {}, // ← 添加以下两行 historyApiFallback: { rewrites: [ { from: /^\/api/, to: 'http://localhost:8080/api' }, // 代理 API { from: /.*/, to: '/index.html' } // 所有非 API 请求返回 index.html ] } }4.2 现象:后端启动报错java.lang.ClassNotFoundException: javax.servlet.Filter
原因:SpringBoot 2.7 默认使用 Tomcat 9,但某些 JDK 8 环境缺少servlet-api.jar。
解决:在wuye-admin/pom.xml的<dependencies>中强制引入:
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency>4.3 现象:MySQL 8.0 连接失败,报错Public Key Retrieval is not allowed
原因:MySQL 8.0 默认禁用公钥检索,而mysql-connector-java5.1.x(项目所用)不支持新认证插件。
解决:二选一
- 方案 A(推荐):降级 MySQL 到 5.7
- 方案 B:升级驱动,在
application-dev.yml的url后追加参数:
url: jdbc:mysql://localhost:3306/wuye_db?...&allowPublicKeyRetrieval=true&useSSL=false并修改pom.xml中mysql-connector-java版本为8.0.33。
4.4 现象:报修图片上传失败,后端报MultipartHttpServletRequest为空
原因:前端repair/submit接口未用FormData构造请求,而是直接传 JSON。
解决:检查src/api/repair.js中提交方法:
// ✅ 正确:用 FormData 上传文件 export function submitRepair(data) { const formData = new FormData() formData.append('title', data.title) formData.append('content', data.content) if (data.image) { formData.append('image', data.image) // image 是 File 对象 } return service.post('/repair/submit', formData, { headers: { 'Content-Type': 'multipart/form-data' } // axios 会自动设置 boundary }) }玄学提示:
headers中不要手动写Content-Type,让 axios 自动处理,否则 boundary 会错。
4.5 现象:登录成功后,前端持续请求/api/user/info返回 401
原因:JWT Token 解析失败,常见于JwtAuthenticationFilter中getUsernameFromToken()方法未正确提取 subject。
解决:检查wuye-common/src/main/java/com/wuye/jwt/JwtUtil.java:
public static String getUsernameFromToken(String token) { try { Claims claims = Jwts.parser() .setSigningKey(SECRET) // ← 确保 SECRET 与 JwtConfig 一致 .parseClaimsJws(token) .getBody(); return claims.getSubject(); // ← 必须是 setSubject(username) 时存入的值 } catch (Exception e) { return null; } }并在登录接口LoginController.login()中确认:
String token = JwtUtil.generateToken(user.getUsername()); // ← 传入的是 username 字段,不是 id5. 功能扩展实战:给报修模块增加图片预览与多图上传能力
5.1 后端改造:支持多图上传与缩略图生成
原项目仅支持单图上传,我们扩展为最多 5 张图片,并自动生成 200x200 缩略图。
第一步:修改RepairController.submit()方法
@PostMapping("/submit") public Result submit(@RequestParam("title") String title, @RequestParam("content") String content, @RequestParam(value = "images", required = false) MultipartFile[] images) { RepairInfo repair = new RepairInfo(); repair.setTitle(title); repair.setContent(content); repair.setCreateTime(new Date()); // 保存多图 if (images != null && images.length > 0) { List<String> imageUrls = new ArrayList<>(); for (MultipartFile file : images) { if (!file.isEmpty()) { String url = fileService.uploadFile(file); // 复用原有 fileService imageUrls.add(url); } } repair.setImageUrls(JSON.toJSONString(imageUrls)); // 存 JSON 字符串 } repairService.save(repair); return Result.success(); }第二步:增强FileService.uploadFile(),增加缩略图
@Service public class FileService { private static final String UPLOAD_PATH = "D:/wuye/uploads/"; // Windows 路径,Linux 改为 /home/wuye/uploads/ public String uploadFile(MultipartFile file) { try { String originalName = file.getOriginalFilename(); String ext = originalName.substring(originalName.lastIndexOf(".")); String uuidName = UUID.randomUUID().toString() + ext; String fullPath = UPLOAD_PATH + uuidName; // 创建缩略图 BufferedImage originalImage = ImageIO.read(file.getInputStream()); BufferedImage thumbnail = Scalr.resize(originalImage, 200, 200); ImageIO.write(thumbnail, "jpg", new File(UPLOAD_PATH + "thumb_" + uuidName)); // 保存原图 file.transferTo(new File(fullPath)); return "/uploads/" + uuidName; // 返回可访问的 URL 路径 } catch (Exception e) { throw new RuntimeException("文件上传失败", e); } } }注意:
Scalr依赖需在pom.xml中添加:
<dependency> <groupId>org.imgscalr</groupId> <artifactId>imgscalr-lib</artifactId> <version>4.2</version> </dependency>5.2 前端改造:Vue 组件支持多图选择与预览
第一步:修改RepairSubmit.vue模板
<template> <el-form :model="form" label-width="80px"> <!-- 其他字段 --> <el-form-item label="上传图片"> <el-upload class="upload-demo" action="" :http-request="handleUpload" :on-preview="handlePreview" :on-remove="handleRemove" :file-list="fileList" list-type="picture-card" :limit="5" :on-exceed="handleExceed" > <i class="el-icon-plus"></i> </el-upload> <div slot="tip" class="el-upload__tip">最多上传 5 张,支持 JPG/PNG</div> </el-form-item> </el-form> </template>第二步:添加 JS 方法
export default { data() { return { form: { title: '', content: '' }, fileList: [] // 已选图片列表 } }, methods: { handleUpload({ file }) { // 将 File 对象存入 fileList,不立即上传 this.fileList.push(file) }, handlePreview(file) { // 预览本地图片 const reader = new FileReader() reader.readAsDataURL(file.raw) reader.onload = () => { window.open(reader.result) // 弹窗预览 } }, handleRemove(file, fileList) { // 从 fileList 中移除 const index = this.fileList.findIndex(f => f.uid === file.uid) if (index !== -1) { this.fileList.splice(index, 1) } }, handleExceed(files) { this.$message.warning(`最多只能上传 5 张图片,当前选择了 ${files.length} 张`) }, async submitForm() { const formData = new FormData() formData.append('title', this.form.title) formData.append('content', this.form.content) // 附加所有图片 this.fileList.forEach(file => { formData.append('images', file.raw) // raw 是原始 File 对象 }) try { await submitRepair(formData) // 调用 api/repair.js 中的 submitRepair this.$message.success('提交成功') this.$router.push('/repair/list') } catch (err) { this.$message.error('提交失败:' + err.message) } } } }5.3 Nginx 静态资源映射:让上传图片可直接访问
后端将图片存到D:/wuye/uploads/,需通过 Nginx 暴露为 HTTP 资源。
编辑nginx.conf,在server块内添加:
location /uploads/ { alias D:/wuye/uploads/; # Windows 路径,Linux 改为 /home/wuye/uploads/ expires 1d; add_header Cache-Control "public, no-transform"; }重启 Nginx 后,图片 URL 如http://localhost/uploads/abc123.jpg即可直接访问。
从那以后我每次做毕设,都强制走一遍「上传-预览-下载-删除」全链路:先用 Postman 测试后端接口,再用浏览器开发者工具看 Network 请求头是否带 Token,最后用 Fiddler 抓包确认图片二进制流是否完整。这套动作看似繁琐,但能提前暴露 90% 的部署问题——比如某次我发现
file.transferTo()在 Linux 上因权限不足静默失败,就是靠抓包发现响应体为空才定位到的。希望帮到你。
本文还有配套的精品资源,点击获取