ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于SpringBoot+Vue的物业管理系统前后端分离实战

基于SpringBoot+Vue的物业管理系统前后端分离实战 简介这份资源是基于Spring Boot与Vue.js的物业管理系统完整源码包采用前后端分离架构适合Java课程设计、毕业设计及项目架构学习者参考。后端以Spring Boot提供数据接口与服务前端用Vue组件化构建交互界面两者通过API通信便于理解企业级Web项目的分层设计与协作方式。压缩包共2025个文件约13.1MB其中svg与scss、css、less等样式资源占比较大用于界面图标与主题美化js与html构成前端页面逻辑32个java文件承载后端核心业务另含sql脚本、properties配置、xml映射及少量pdf、xmind文档可辅助梳理数据库与项目结构。目前已有87人学习下载。读者可从中获取一套可运行的物业管理系统范例观察前后端目录划分、接口调用与静态资源组织方式并借助配置与脚本快速搭建本地环境适合作为课程设计参考或架构入门练手素材。1. 物业管理系统为什么值得用 SpringBoot Vue 重写一遍很多中小物业公司还在用 Excel 加微信群管收费、报修和巡检数据散落在十几个表格里月底对账全靠人工翻聊天记录。这套「基于 SpringBoot Vue 的物业管理系统前后端分离模式」要解决的正是这个问题把房产档案、业主信息、收费单、报修工单、巡检记录收进一个后台前端用 Vue 做交互后端用 SpringBoot 提供接口两边通过 HTTP 加 JSON 通信。它适合两类人一是想拿一个真实业务练手前后端分离的开发者二是物业信息化负责人想评估自研成本。我做过两版类似的系统第一版前后端揉在一起改一个页面要重新打包整个 war第二版拆开之后前端改样式后端不用重启联调效率至少翻倍。这篇笔记就按「怎么搭、怎么拆、坑在哪」的顺序把可复现的路径讲清楚。2. 前后端分离的边界怎么划接口契约先于代码2.1 为什么物业系统特别适合前后端分离物业业务的读写比例很悬殊业主端查账单、查公告是高频只读管理端录费用、派工单是低频写入。前后端分离之后只读接口可以单独加缓存写入接口单独做事务和幂等互不干扰。如果还是 JSP 或 Thymeleaf 那种服务端渲染每次查账单都要走一遍完整请求生命周期页面里还混着 Java 逻辑改个按钮颜色都得动后端工程。另一个现实原因是终端多。物业场景里管理端在 PC 浏览器保安巡检可能用平板业主报修用手机。Vue 打包出来的静态资源可以同时喂给这三类终端SpringBoot 只负责吐 JSON不用为每种终端写一套模板。这就是「前后端分离项目实战」里最核心的收益一次接口开发多端复用。2.2 接口契约先定别急着写 Controller我一般会先拉一张接口清单把资源路径、方法、入参、出参、错误码定死再动手写代码。物业系统的核心资源就那么几个/api/houses、/api/owners、/api/bills、/api/repairs、/api/inspections。下面是一个统一的返回体约定前后端都按这个结构解析能省掉大量扯皮。// 统一响应体放在 common 包下所有 Controller 返回它 public class ResultT { private int code; // 200 成功401 未登录403 无权限500 业务异常 private String message; // 给前端直接展示的提示语 private T data; // 业务数据列表接口放 PageResult public static T ResultT ok(T data) { ResultT r new Result(); r.code 200; r.message success; r.data data; return r; } public static T ResultT fail(int code, String message) { ResultT r new Result(); r.code code; r.message message; return r; } }这段代码的关键在于code和 HTTP 状态码分离HTTP 永远返回 200业务成败看code。这样做的好处是前端拦截器只需判断code不用同时处理网络层和业务层两套错误。参数上message要写成能直接弹给用户看的中文比如「该房产已存在未缴账单」而不是「bill conflict」。2.3 分页和查询参数怎么统一列表接口最容易各写各的。我一般约定分页参数固定用pageNum和pageSize查询条件用业务字段名直接传比如ownerName、houseNo、status。后端用 MyBatis-Plus 的Page对象接前端用 Element Plus 的el-pagination接字段名对齐就不用写转换层。// 分页查询账单pageNum 从 1 开始pageSize 默认 10 GetMapping(/bills) public ResultPageResultBillVO listBills( RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize, RequestParam(required false) String houseNo, RequestParam(required false) Integer status) { PageBill page new Page(pageNum, pageSize); LambdaQueryWrapperBill wrapper new LambdaQueryWrapper(); wrapper.like(StringUtils.hasText(houseNo), Bill::getHouseNo, houseNo) .eq(status ! null, Bill::getStatus, status) .orderByDesc(Bill::getCreateTime); PageBill result billMapper.selectPage(page, wrapper); return Result.ok(PageResult.of(result)); }pageNum从 1 开始是前端分页组件的默认习惯如果后端用 0 开始前端每次都要减一容易漏。like和eq前面加条件判断是为了让空参数不参与 SQL 拼接避免where house_no like %%这种全表扫描。orderByDesc按创建时间倒序保证新账单永远在第一页。3. 后端落地SpringBoot 工程结构与核心模块3.1 包结构按业务分别按技术分层很多教程教人按controller、service、mapper顶层分包项目小的时候没问题物业系统模块一多找「报修」相关代码要横跨四个包。我一般按业务域分house、owner、bill、repair、inspection每个域下面再放自己的 controller、service、mapper、entity。这样改报修功能只进repair一个包边界清晰。com.example.property ├── common // Result、PageResult、全局异常、工具类 ├── config // 跨域、拦截器、MyBatis-Plus 配置 ├── house // 房产档案 │ ├── controller │ ├── service │ ├── mapper │ └── entity ├── owner // 业主信息 ├── bill // 收费管理 ├── repair // 报修工单 └── inspection // 巡检记录这个结构的好处是将来如果某个模块要拆成独立服务整个包搬走就行依赖关系一目了然。common和config保持稳定业务包之间尽量不互相引用需要跨域数据时通过 service 接口调用。3.2 跨域和登录拦截怎么配前后端分离开发阶段前端跑在localhost:5173后端跑在localhost:8080浏览器会拦跨域请求。常见做法是后端加一个全局 CORS 配置而不是在每个 Controller 上贴CrossOrigin。Configuration public class WebConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOriginPatterns(*) // 开发阶段放开生产改成具体域名 .allowedMethods(GET, POST, PUT, DELETE) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new AuthInterceptor()) .addPathPatterns(/api/**) .excludePathPatterns(/api/auth/login, /api/auth/captcha); } }allowedOriginPatterns而不是allowedOrigins是因为后者在allowCredentials(true)时不允许用*。maxAge(3600)让预检请求缓存一小时减少 OPTIONS 请求。拦截器排除登录和验证码接口其余接口都要带 token。生产环境一定要把allowedOriginPatterns改成实际域名否则等于没做跨域限制。3.3 收费单生成一个必须做幂等的写接口物业收费是核心每月批量生成账单时如果网络抖动导致前端重复提交就会产生重复账单。我一般用「房产编号 费用周期」做唯一索引插入时用insert ignore或捕获唯一键冲突。Transactional(rollbackFor Exception.class) public int generateMonthlyBills(String period) { ListHouse houses houseMapper.selectList(null); int count 0; for (House house : houses) { Bill bill new Bill(); bill.setHouseNo(house.getHouseNo()); bill.setPeriod(period); // 格式 2025-06 bill.setAmount(house.getArea().multiply(UNIT_PRICE)); bill.setStatus(0); // 0 未缴1 已缴 try { billMapper.insert(bill); count; } catch (DuplicateKeyException e) { // 该房产该周期已生成跳过保证幂等 } } return count; }period用2025-06这种字符串比存两个整数字段查询方便。唯一索引建在(house_no, period)上数据库层面兜底。Transactional保证要么全成功要么全回滚但注意捕获DuplicateKeyException后事务不会标记回滚因为异常被吞了这是有意为之——跳过重复项继续生成其他房产。4. 前端落地Vue 工程搭建与关键页面4.1 用 Vite 起项目别再用老脚手架现在起 Vue 项目我一般直接用 Vite冷启动和热更新比老方案快一个量级。命令很简单选 Vue JavaScript 或 TypeScript 都行物业系统字段多我倾向 TypeScript能在编译期挡住不少字段名拼写错误。npm create vitelatest property-web -- --template vue-ts cd property-web npm install npm install element-plus axios vue-router pinia npm run dev装完依赖后main.ts里挂载 Element Plus 和路由。axios做请求封装pinia管登录状态和全局字典。开发服务器默认 5173 端口和后端 8080 通过代理打通避免跨域配置反复改。4.2 axios 封装统一带 token 和错误提示前端每个页面都写一遍axios.get加 token 加错误处理是维护灾难。我一般封装一个request.ts请求拦截器塞 token响应拦截器统一判断code。import axios from axios import { ElMessage } from element-plus import router from /router const request axios.create({ baseURL: /api, timeout: 10000 }) request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) request.interceptors.response.use( response { const res response.data if (res.code 200) { return res.data } if (res.code 401) { localStorage.removeItem(token) router.push(/login) } ElMessage.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) }, error { ElMessage.error(网络异常请稍后重试) return Promise.reject(error) } ) export default requestbaseURL设为/api配合 Vite 的server.proxy转发到后端生产环境用 Nginx 做同样转发。响应拦截器里code 200时直接返回res.data页面里就不用每次写res.data.data。401 时清 token 跳登录这是前后端分离项目里最常见的会话过期处理。4.3 动态路由和菜单权限物业系统里管理员、收费员、保安看到的菜单不一样。我一般用动态路由登录后后端返回该用户的菜单树前端根据菜单树过滤路由表。// 根据后端返回的菜单权限过滤路由 function filterRoutes(routes: RouteRecordRaw[], menus: string[]): RouteRecordRaw[] { return routes.filter(route { if (route.meta?.permission) { return menus.includes(route.meta.permission as string) } if (route.children) { route.children filterRoutes(route.children, menus) } return true }) }meta.permission是路由和权限的绑定键后端菜单表里存同样的字符串。过滤后调用router.addRoute动态挂载。注意 404 页面要最后加否则动态路由还没挂上就被 404 捕获了这是新手最容易翻车的地方。5. 避坑与排查联调阶段最常见的 5 个问题5.1 跨域配置了还是报 CORS 错误现象后端加了 CORS 配置浏览器控制台仍然报No Access-Control-Allow-Origin header。原因通常是拦截器在 CORS 之前执行或者allowCredentials(true)和allowedOrigins(*)冲突。解决用allowedOriginPatterns并确保 CORS 配置的优先级高于自定义拦截器必要时把拦截器里的 OPTIONS 请求直接放行。5.2 前端打包后刷新页面 404现象npm run build后部署到 Nginx首页能打开刷新/bill/list就 404。原因是 Vue Router 的 history 模式需要服务端把所有路径回退到index.html。解决Nginx 加try_files $uri $uri/ /index.html;。如果不想改 Nginx就把路由模式改成 hash但 URL 会带#看个人取舍。5.3 日期字段前后端差 8 小时现象后端返回2025-06-01T00:00:00前端显示成2025-05-31 16:00。原因是 Jackson 默认按 UTC 序列化前端按本地时区解析。解决在application.yml里配spring.jackson.time-zone: GMT8并在实体字段上加JsonFormat(pattern yyyy-MM-dd HH:mm:ss, timezone GMT8)。两边都配才稳。5.4 批量生成账单时部分成功部分失败现象生成 100 条账单结果只插入了 60 条日志里有唯一键冲突。原因是没做幂等重复周期重复插入。解决建(house_no, period)唯一索引代码里捕获DuplicateKeyException跳过或者插入前先查一次已存在的房产编号集合批量过滤。前者更可靠因为查和插之间有并发窗口。5.5 前端路由跳转后页面数据不刷新现象从账单列表点进详情再返回列表数据还是旧的。原因是组件被缓存或路由复用了同一个组件实例onMounted没重新触发。解决给router-view加:key$route.fullPath强制重建或者在onActivated里重新拉数据。物业系统列表页多我一般用后者配合keep-alive保留滚动位置。6. 把系统跑稳的一个进阶习惯接口自测脚本先于前端联调后端接口写完别急着喊前端联调。我一般先用curl或 HTTP 文件把核心链路跑一遍确认登录、查房产、生成账单、派工单这四条路径的入参出参都对再交给前端。这样联调时前端报的错大概率是前端问题责任边界清楚省掉大量「到底是哪边的问题」的扯皮。# 登录拿 token TOKEN$(curl -s -X POST http://localhost:8080/api/auth/login \ -H Content-Type: application/json \ -d {username:admin,password:123456} | jq -r .data.token) # 用 token 查房产列表验证鉴权链路 curl -s http://localhost:8080/api/houses?pageNum1pageSize5 \ -H Authorization: Bearer $TOKEN | jq .data.total # 生成 2025-06 账单验证幂等 curl -s -X POST http://localhost:8080/api/bills/generate \ -H Authorization: Bearer $TOKEN \ -H Content-Type: application/json \ -d {period:2025-06} | jq .datajq用来提取 JSON 字段-r去掉引号方便赋值给变量。第二次执行生成账单命令时返回的data应该是 0 或跳过数量而不是报错这就验证了幂等生效。这套脚本我一般存成api-test.sh放进仓库换台机器也能快速验证环境。还有一个习惯所有涉及金额的字段数据库用decimal(10,2)Java 用BigDecimal前端展示前统一toFixed(2)。我见过用double存物业费累加几十条之后出现0.30000000000000004的尾差对账时被财务追着问。这种坑踩一次就够记一辈子。把接口自测和金额精度这两件事做成肌肉记忆系统上线后的返工量会少很多。希望帮到你。本文还有配套的精品资源点击获取
返回列表