十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

SpringBoot+Vue+MySQL物业管理系统全栈实战:从数据库设计到部署

SpringBoot+Vue+MySQL物业管理系统全栈实战:从数据库设计到部署 做了这么多年Java后端SpringBoot那套东西闭着眼都能搭但真正让我觉得“这系统能落地”而不是“这代码能跑”的反而是去年帮朋友小区做的一个物业管理系统。项目标题就叫“Spring boot名城小区物业管理系统信息管理系统源码-SpringBoot后端Vue前端MySQL【可直接运行】”。别看名字长其实核心就一句话用SpringBoot把后端接口理清楚用Vue把操作界面做顺手再用MySQL把业主、房屋、费用、工单这些数据稳稳存住。这篇文章我不聊虚的直接从需求拆解、数据库设计、后端核心实现、前端页面联调一直讲到部署踩坑把这套系统从头到尾给你捋一遍。这套东西适合谁看如果你是刚学完SpringBoot和Vue、想找一个完整项目练手的学生或者你是中小型物业公司里负责信息化的人想找一个能直接跑起来、能改着用的系统那这篇内容就是给你准备的。完整弄懂它你不仅知道代码怎么组织还能明白“为什么这么设计”以后再接类似的管理系统项目心里就有底了。1. 项目整体需求分析与技术选型1.1 物业管理系统的核心业务场景先说说这个小区的实际背景。名城小区是一个中等规模、大约1500户的住宅小区物业公司日常要管的事情非常杂业主信息要登记房屋楼栋要关联每个月要收物业费、停车费业主家里水管漏了、灯泡坏了要报修访客车辆进小区要登记还有公告通知要发出去。在没有系统之前这些工作全靠Excel表格加微信群。业主信息表和房屋表是分开的两个文件要对上号得靠人工用房号匹配物业费催缴靠手工记账谁交了谁没交月底一翻聊天记录容易漏报修单写在小纸条上师傅修没修、修完有没有回访全靠自觉。所以这个系统最核心的价值就是把“人找事”变成“事找人”——所有业务都有记录、有状态、有流程。基于这个场景系统必须覆盖以下核心模块业主信息管理业主基本资料、联系方式、身份证号、与房屋绑定关系。房屋与楼栋管理小区、楼栋、单元、房号的层级结构房屋状态未售、已入住、空置。物业费用管理费用项定义物业费、水费、停车费、账单生成、缴费记录、欠费统计。报修工单管理业主提交报修、物业派单、维修工接单、完成反馈、业主评价。车位与车辆管理车位信息、业主车辆绑定、访客车辆登记。公告通知管理物业发布公告业主端查看。系统用户管理管理员、物业人员、维修工、业主等不同角色的登录与权限。说白了这就是一个典型的企业级CRUD系统加几条业务流程。但别小看CRUDCRUD设计得好不好直接决定这个系统后期能不能维护。1.2 为什么选SpringBootVueMySQL这套组合选型的时候我也考虑过若依这种现成的快速开发平台也考虑过用Thymeleaf做服务端渲染。但最终定了SpringBoot Vue MySQL这套组合理由有三点。第一前后端分离是现在的主流。物业公司虽然内部用但后续肯定会有业主端小程序、公众号H5这些接入需求。如果前后端不分后端接口没法被小程序复用。用Vue做前端后端只暴露JSON接口以后要接什么端都方便。第二SpringBoot太成熟了。做这套系统用到的技术点——Spring Security做认证、MyBatis-Plus操作数据库、Redis做缓存——每一个都有海量资料遇到问题搜一下就能解决不担心卡住。对于一个小型管理系统来说SpringBoot的稳定性和开发效率都是最优解。第三团队技术栈匹配。这套系统的维护方是物业公司合作的软件外包团队他们Java和Vue都能写MySQL用得也熟。选这套技术栈将来二次开发不用换人、不用重新学这是很现实的考量。1.3 系统架构与目录结构规划系统整体是标准的前后端分离架构。后端跑在8080端口前端开发时跑在8081端口通过Vite代理转发请求生产环境后端打成Jar包前端build之后由Nginx托管静态文件然后反向代理到后端接口。后端项目结构按功能模块分包而不是按技术类型分包这一点非常重要。我见过很多新手把Controller放一个包、Service放一个包结果代码一多改一个功能要跳三四个目录。我采用的是按业务模块拆分com.mingcheng.property ├── common // 通用类统一返回结果、异常处理、工具类 ├── config // 配置类Security、MyBatis-Plus、Redis、CORS ├── controller // 控制层按业务模块分OwnerController、HouseController、FeeController... ├── service // 业务接口与实现 ├── mapper // MyBatis-Plus的Mapper接口 ├── entity // 数据库实体类 ├── dto // 前端传入的参数对象 ├── vo // 返回给前端的视图对象 └── security // JWT认证相关过滤器、Token工具类、UserDetailsService前端用Vue3 Vite Element Plus Pinia工程结构是标准的分层方式src ├── api // 按模块封装的后端请求 ├── assets // 静态资源 ├── components // 公共组件 ├── router // 路由配置含动态路由与权限守卫 ├── store // Pinia状态管理存用户信息与登录态 ├── utils // 工具类request封装、格式化 └── views // 页面组件按业务模块分目录这样分层之后一个业务功能从前端页面到后端接口链路非常清楚。后面我们讲到具体功能的时候就是沿着这条链路往下走的。2. 数据库设计与核心表结构2.1 业务实体梳理与关联关系设计数据库设计是整个系统最关键的环节。我的习惯是先把业务实体全部列出来梳理出它们之间的关系再动手建表。这套系统核心实体有用户、业主、房屋、小区楼栋、费用项、账单、缴费记录、报修工单、车位、车辆、公告、角色权限。这里我重点讲一下几个容易踩坑的关联关系。第一是业主和房屋的关系。一个业主名下可以有多套房子一套房子在某个时间段内也有可能有多个居住人比如夫妻共同居住。为了灵活处理我在业主表和房屋表之间设计了关联表owner_house_rel里面除了两个外键之外还有一个is_primary字段标记该业主是否是这套房子的主要联系人。这样以后如果要做“一房多业主”或者“一业主多房”的业务扩展都不用改表结构。第二是用户和业主的关系。业主可能同时在系统里有一个登录账号但也有可能业主压根不用系统留的是家里子女的手机号。所以user表通过owner_id字段和业主表关联允许为空。这样物业人员在后台代业主操作时也不需要强制创建系统账号。第三是账单和缴费记录的关系。一张账单对应多笔缴费记录因为业主可能分多次付款也可能物业人员手工调整金额。所以在缴费记录表里有一个bill_id关联回账单表每次缴费更新账单的paid_amount累计字段而不是覆盖total_amount。2.2 核心表结构详解下面挑几张最核心的表来说。业主信息表t_owner字段名类型说明idbigint主键自增owner_namevarchar(50)业主姓名phonevarchar(20)联系电话id_cardvarchar(18)身份证号加密存储gendertinyint性别 0未知 1男 2女owner_typetinyint业主类型 1个人 2单位create_timedatetime创建时间update_timedatetime更新时间deletedtinyint逻辑删除标记注意owner_type这个字段。小区里有个人业主也可能有公司购买商品房作为员工宿舍这两种业主在催费流程上是不一样的个人业主主要打电话发短信单位业主可能需要发函所以在一开始就要区分开。房屋信息表t_house字段名类型说明idbigint主键building_novarchar(20)楼栋号unit_novarchar(20)单元号room_novarchar(20)房号areadecimal(10,2)建筑面积house_typevarchar(50)户型如三室两厅statustinyint0未售 1已售 2空置 3自住property_fee_ratedecimal(8,2)物业费单价元/平米/月房屋表里我把楼栋号、单元号、房号拆成三个字段而不是合成一个house_full_name这样后期如果要按楼栋筛选房屋、按单元统计费用SQL写起来非常方便。property_fee_rate放在房屋表里是因为不同楼栋的物业费单价可能不一样比如洋房和高层这样生成账单的时候直接取这个字段计算不需要额外配置。账单表t_bill字段名类型说明idbigint主键house_idbigint关联房屋IDbill_typetinyint费用类型 1物业费 2停车费 3其他bill_monthvarchar(7)账单期间如2024-06total_amountdecimal(10,2)应收金额paid_amountdecimal(10,2)已收金额默认0statustinyint0未缴 1部分缴纳 2已缴清 3已作废due_datedate缴费截止日期create_timedatetime生成时间这张表的bill_month字段我特意设计成字符串类型格式是“YYYY-MM”这样查询某个月份的所有账单就很简单WHERE bill_month 2024-06。status字段由程序根据paid_amount和total_amount的比较自动更新而不是让用户手工改状态避免数据不一致。报修工单表t_repair_order字段名类型说明idbigint主键order_novarchar(32)工单编号owner_idbigint报修业主house_idbigint房屋IDcontenttext报修内容描述imagesvarchar(500)上传的图片路径逗号分隔appoint_timedatetime期望上门时间assignee_idbigint维修工IDstatustinyint0待派单 1已派单 2维修中 3已完成 4已取消 5已评价complete_timedatetime完成时间evaluationvarchar(500)业主评价scoretinyint评分1-5工单的status状态流转是这套系统业务流程里最有意思的部分。我用一个状态机来控制流转待派单可以变为已派单或已取消已派单可以变为维修中维修中可以变为已完成完成后可以变为已评价。非法状态转换后端会直接拒绝。这样做的好处是流程不会乱套每一个工单在任何时刻都知道自己处于什么阶段。2.3 初始化数据的准备技巧建完表之后别急着写代码先准备初始化数据。我用一个data.sql脚本里面包含三个角色管理员、物业人员、维修工一个默认管理员账号一栋楼的房屋数据比如1号楼1单元101到1501这里有个小技巧房屋数据如果需要批量生成不要手工一条条INSERT我写了一个简单的Java程序跑一次把生成的INSERT语句打印出来再粘贴到SQL脚本里。这样既不用手动拼SQL又能保证数据格式正确。对于一套演示系统来说楼栋数据不需要太多有两栋楼、每栋两个单元、每个单元十几户就足够演示所有功能了。初始化数据还有一层作用测试接口的时候不需要先花时间造数据。前端开发联调时直接登录管理员账号界面上就有东西可以看、可以点沟通效率高很多。3. 后端核心功能实现详解3.1 基于Spring Security JWT的登录认证认证方式我选了JWT而不是Session原因很简单前后端分离架构下后端接口将来要支持小程序和移动端JWT天然无状态不需要考虑Session共享、跨域Cookie携带这些麻烦事。实现上我用Spring Security JWT Redis的方案做了一套轻量级认证框架没有引入Spring Security OAuth2那一大套东西。流程是用户提交用户名密码到/api/auth/login接口。后端校验账号密码。密码使用BCrypt加密存储校验时用PasswordEncoder.matches()方法。校验通过后用JWT工具类生成tokentoken里只放用户ID和用户名不放过多的业务信息。把token存到Redis里设置过期时间同时间token返回给前端。Redis里的key是login_token:用户IDvalue是token字符串。前端每次请求在Header里带Authorization: Bearer token。后端加一个JWT认证过滤器每次请求先解析token验签通过后把用户信息放入SecurityContext。为什么token要额外存一份到Redis因为JWT本身是无法主动失效的如果用户改了密码或者被管理员踢下线旧的token依然有效这有安全隐患。存了Redis之后每次请求都检查Redis里的token是否和请求带的一致不一致就拒绝。这样虽然多了一次Redis查询但换来的是可控的登录态管理值得。核心代码大概长这样Component public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { Autowired private StringRedisTemplate redisTemplate; Autowired private JwtUtil jwtUtil; Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String authHeader request.getHeader(Authorization); if (StringUtils.hasText(authHeader) authHeader.startsWith(Bearer )) { String token authHeader.substring(7); String username jwtUtil.getUsernameFromToken(token); if (username ! null SecurityContextHolder.getContext().getAuthentication() null) { String redisToken redisTemplate.opsForValue() .get(login_token: username); if (token.equals(redisToken) jwtUtil.validateToken(token)) { UserDetails userDetails userDetailsService.loadUserByUsername(username); UsernamePasswordAuthenticationToken authentication new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); } } } filterChain.doFilter(request, response); } }这段代码里有个细节容易被忽略校验Redis里的token和请求token是否一致之后才放行到后续流程。也就是说管理员在后台重置用户密码时只要删除Redis里的key这个用户立马就掉线了不需要等token自然过期这在真实的运维场景里非常实用。3.2 业主信息管理与房屋绑定逻辑业主管理这个模块表面看就是一张表的增删改查但核心难点在“业主和房屋的绑定关系”上。我设计的是新增业主时同时绑定房屋流程在一个事务里完成新增业主表记录拿到业主ID。接收前端传过来的房屋ID数组逐个插入owner_house_rel关联表。每绑定一套房子同步更新房屋表的status为1已售。这里有个需要决策的点业主删除了怎么办如果直接物理删除那缴费记录、工单记录里的业主信息就会变成悬空引用后台查历史数据时显示不出名字。所以我采用逻辑删除策略。业主表加一个deleted字段默认0删除时置为1。所有查询默认加条件deleted 0。这样历史数据保住了界面上又不显示已删除的业主。还有一个细节身份证号存储。按照国家个人信息保护的要求身份证号不能明文存储。我在数据库里存的是加密后的密文查询时用JsonIgnore注解不让身份证号出现在接口返回里。如果需要展示前端只展示脱敏后的版本格式是110***********1234。这个细节虽然不影响功能但在合规层面是加分项。3.3 物业费用生成与缴费状态管理物业费是这个系统最核心的业务之一。每个月要生成一批账单这是典型的定时任务场景。我用了Spring的Scheduled注解每月1号凌晨自动执行一次账单生成任务。生成逻辑是这样的扫描所有状态为“已售”且有绑定业主的房屋按当前月份生成一张账单金额根据房屋面积和物业费单价自动计算。Component public class BillGenerateTask { Autowired private HouseMapper houseMapper; Autowired private BillMapper billMapper; Scheduled(cron 0 0 6 1 * ?) // 每月1号6点执行 Transactional(rollbackFor Exception.class) public void generateMonthlyBill() { // 获取上月账单期间如2024-06 String lastMonth LocalDate.now() .minusMonths(1) .format(DateTimeFormatter.ofPattern(yyyy-MM)); // 查询所有已售房屋 ListHouse houses houseMapper.selectByStatus(1); for (House house : houses) { // 如果这个月的账单已存在跳过避免重复生成 Bill exist billMapper.selectByHouseAndMonth(house.getId(), lastMonth); if (exist ! null) { continue; } BigDecimal amount house.getArea() .multiply(house.getPropertyFeeRate()); Bill bill new Bill(); bill.setHouseId(house.getId()); bill.setBillType(1); bill.setBillMonth(lastMonth); bill.setTotalAmount(amount); bill.setPaidAmount(BigDecimal.ZERO); bill.setStatus(0); bill.setDueDate(LocalDate.now().plusDays(30)); // 缓冲期30天 billMapper.insert(bill); } } }关键点有两个。一个是幂等性任务执行前先检查本月账单是否已存在防止因为手动触发或重复部署导致账单重复生成。另一个是账期设置每月1号生成的是上个月的账单。为什么要这样业主6月住的房子7月初才出6月的账这符合小区缴费的习惯。缴费状态管理上我利用了MyBatis-Plus的乐观锁功能。缴费记录表t_payment插入一条缴费流水时执行UPDATE t_bill SET paid_amount paid_amount #{amount}, update_time NOW() WHERE id #{billId}然后根据最新金额更新账单状态。这是一个很经典的资金操作场景虽然这套系统不涉及真实的第三方支付但数据结构上已经预留了接入微信支付、支付宝的扩展空间。3.4 报修工单的全流程处理报修工单模块是这个系统里业务流程最完整的一个从业主提交到最终评价一共经历5个状态。我在后端Service层用状态机做约束不允许跳状态更新。业主提交报修工单的接口PostMapping(/api/repair/commit) public Result commitRepair(RequestBody RepairCommitDTO dto) { // 参数校验报修内容、联系电话必填 RepairOrder order new RepairOrder(); order.setOwnerId(dto.getOwnerId()); order.setHouseId(dto.getHouseId()); order.setContent(dto.getContent()); order.setImages(dto.getImages()); order.setAppointTime(dto.getAppointTime()); order.setStatus(0); // 待派单 order.setOrderNo(generateOrderNo()); repairOrderMapper.insert(order); return Result.success(); }generateOrderNo()方法生成工单号格式是BX加时间戳再加三位随机数保证每个工单号全局唯一。这样做的好处是维修工在电话里报工单号后台直接用这个号就能查到记录不需要查手机号或房号。派单是物业人员的操作从待派单列表里选择工单指定维修工。这里我注意到一个业务细节维修工也是有登录账号的角色所以派单时直接传维修工的用户ID而不只是一个名字。这样维修工登录系统后能通过assignee_id查询到属于自己的待办工单。维修工接单、完成工单经过状态校验后更新状态为已完成。最后业主对完成的工单进行评价评价内容包括评分和文字评价。状态更新核心方法如下Transactional(rollbackFor Exception.class) public void updateStatus(Long orderId, Integer targetStatus, Long operatorId) { RepairOrder order repairOrderMapper.selectById(orderId); if (order null) { throw new BizException(工单不存在); } // 校验操作者身份 if (targetStatus 2 || targetStatus 3) { if (!order.getAssigneeId().equals(operatorId)) { throw new BizException(只有接单的维修工才能执行该操作); } } // 校验状态流转合法性 if (!validStatusTransition(order.getStatus(), targetStatus)) { throw new BizException(非法状态流转); } order.setStatus(targetStatus); if (targetStatus 3) { order.setCompleteTime(LocalDateTime.now()); } repairOrderMapper.updateById(order); }状态流转校验是这套系统里我个人最满意的一部分。它让整个业务链条在代码层面形成了闭环而不是靠操作员自觉去维护流程规范。4. 前端Vue页面实现与交互4.1 前端工程结构与路由配置前端部分我用的是Vue3 Vite Element Plus Pinia这套组合。Vite作为构建工具开发环境下启动速度比Webpack快得多热更新也很灵敏。Element Plus提供了现成的表格、表单、对话框组件对于做后台管理系统来说特别省事。路由配置上我设置了登录页和主布局页两个层级。主布局页内包含三个一级页面业主管理、费用管理、报修管理。考虑到演示版本的简洁性没有做动态路由所有页面都在静态路由表里。每个模块的代码按功能放在views目录下以业主管理为例src/views/owner/ ├── index.vue // 业主列表页 ├── OwnerForm.vue // 新增/编辑业主对话框 └── OwnerHouse.vue // 业主房屋绑定对话框组件拆分的原则是列表页只负责展示和调用接口表单和绑定关系这类可复用的逻辑提取成子组件。这样业主列表页的代码保持清爽后期如果要做业主详情页可以复用OwnerForm.vue。4.2 核心页面实现业主管理页面主体是一张表格。页面加载时调用/api/owner/list接口参数是分页信息和搜索关键词。表格展示业主姓名、联系电话、绑定房屋数、创建时间操作栏放“编辑”“绑定房屋”“删除”三个按钮。这里的绑定房屋数后端在返回列表时直接用LEFT JOIN统计好了避免前端对每条记录再发一次请求。费用管理页面相对复杂一点。顶部是搜索栏和“生成当月账单”按钮中间是账单列表。列表包含房屋信息、账单月份、应收金额、已收金额、状态标签、操作按钮。状态标签用了Element Plus的el-tag组件未缴显示灰色、部分缴纳显示橙色、已缴清显示绿色。视觉上非常直观物业人员扫一眼就知道哪栋楼的缴费率低。报修工单页面拆成了三个Tab待派单、进行中、已完成。每个Tab里是不同状态的工单卡片卡片上显示报修内容、报修人电话、期望时间、当前状态。这样的设计贴近真实使用习惯维修工登录后只看“进行中”这个Tab就知道自己要干哪些活。这里要提一个Vue的实践技巧列表页数据在切换Tab时建议重新请求而不是只在前端做过滤。虽然前端过滤看起来快但如果有分页就必须由后端来筛选否则只能筛当前页的数据。所以我的三个Tab对应三个不同的状态参数每次切换都重新调/api/repair/list?statusxxx接口。4.3 Axios封装与后端API对接Axios请求封装是前后端联调的关键环节。我封装了一个request.js在拦截器里统一做三件事加上JWT请求头、统一处理返回码、统一处理HTTP错误。import axios from axios import { ElMessage } from element-plus import router from /router import { useUserStore } from /store/user const request axios.create({ baseURL: /api, timeout: 10000 }) // 请求拦截器带上token request.interceptors.request.use(config { const userStore useUserStore() if (userStore.token) { config.headers.Authorization Bearer ${userStore.token} } return config }) // 响应拦截器统一处理错误 request.interceptors.response.use( response { const res response.data if (res.code ! 200) { ElMessage.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) } return res.data }, error { if (error.response error.response.status 401) { ElMessage.error(登录已过期请重新登录) const userStore useUserStore() userStore.logout() router.push(/login) } else { ElMessage.error(error.message || 网络异常) } return Promise.reject(error) } )这个封装的精髓在于业务代码里不需要每个接口都写错误处理统一拦截器已经处理了。而且后端所有接口的返回格式统一是{ code: 200, message: success, data: {...} }前端拿到response.data之后直接就是业务数据不用再解一层。联调的时候我和后端约定好这个格式两边各写各的一次对接就通了。5. 部署运行与常见问题排查5.1 本地环境搭建与项目启动步骤这套系统要跑起来需要准备的环境包括JDK 1.8及以上、Maven 3.6及以上、Node.js 16及以上、MySQL 5.7或8.0。具体启动步骤我整理一下新建数据库property_mgmt执行项目里的sql/init.sql脚本完成建表和初始化数据。修改后端application.yml里的数据库用户名、密码、Redis地址。在后端根目录执行mvn spring-boot:run看到“Started Application”日志说明后端启动成功。前端进入frontend目录依次执行npm install和npm run dev。浏览器访问http://localhost:8081用初始化脚本中的管理员账号登录。如果按照这个过程走完还起不来大概率是下面几个问题之一。5.2 常见启动报错与解决实录我实际部署中遇到最多的一个问题是端口冲突。电脑上以前跑过别的Java服务占用了8080端口后端启动直接报Port 8080 was already in use。解决办法很简单要么把那个进程杀掉要么在application.yml里面改端口server: port: 9090改完端口后记得前端vite.config.js里的代理target也要同步改否则前端请求还是会打到8080。第二个高频问题是我数据库连接不上。报错内容是Access denied for user rootlocalhost原因通常是MySQL密码和配置文件里的不一致。还有一次是MySQL8.0和旧版驱动的兼容性问题报Public Key Retrieval is not allowed。这个错误的根源是MySQL 8.0默认使用caching_sha2_password认证方式旧版驱动不支持。解决办法是在JDBC连接串上加上allowPublicKeyRetrievaltrueurl: jdbc:mysql://localhost:3306/property_mgmt?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue第三个坑是前端npm install时依赖版本冲突。Element Plus和Vite都升级得很快如果直接把最新的版本都装上有时会出现样式丢失或者运行时警告。我在package.json里固定了版本号确保每次安装的依赖一致。5.3 项目二次开发与扩展建议系统跑通之后我给它规划了三条扩展方向这也是一开始设计时就预留的接口。第一是接入业主微信小程序。后端所有接口已经按RESTful风格设计好小程序端只需要复用已有的/api/owner、/api/repair、/api/bill接口即可。需要补充的是小程序端的登录认证逻辑可以用微信的code2session换openid然后和业主手机号做绑定。这个扩展把业主从“被动接电话”变成“主动查账单、提报修”对物业公司提升服务感知非常有帮助。第二是接入第三方支付。数据库的账单表和缴费记录表已经支持部分缴费和多次缴费接第三方支付只需要增加一个支付回调接口收到回调后调用PaymentService.pay()方法业务层面不需要改动。第三是增加通知推送能力。目前系统内公告是业主登录后自己查看很被动。可以扩展一个通知消息表把缴费提醒、工单状态变更、公告发布都写入消息表再对接短信服务商或者微信模板消息推送。这不影响现有表结构只需要加一张消息表和一个推送任务。6. 数据可视化与日常运维功能6.1 物业费收缴率统计系统跑了一段时间后物业经理提了一个新需求想看缴费进度。这时候如果还靠excel导出来算就没有意义了。我在前端加了一个首页Dashboard展示核心运营数据——物业费收缴率、待处理工单数、本月投诉量、入住率。数据通过后端统计接口实时返回。收缴率统计的SQL大概是这样的思路SELECT bill_month, SUM(total_amount) AS total, SUM(paid_amount) AS paid, ROUND(SUM(paid_amount) / SUM(total_amount) * 100, 2) AS rate FROM t_bill WHERE bill_month 2024-06 GROUP BY bill_month这套统计接口的数据直接从账单表聚合查询不需要额外建统计表因为物业管理系统的数据量级几千上万条账单完全在MySQL单表聚合的能力范围内。但如果后续要保存历史快照做趋势分析可以考虑每天定时把统计数据写入独立的统计表。6.2 基于Vue的播放与文件上传场景扩展原本的物业系统里没有视频相关的功能但后来物业说要做一个社区公告视频的展示业主可以在系统里查看物业发布的宣传视频。这时候就用到了Vue播放m3u8流媒体视频的能力。具体做法是通过video.js插件配合videojs-contrib-hls来播放m3u8格式的视频流。在Vue组件里注册插件后直接传入m3u8地址就能播放import videojs from video.js import video.js/dist/video-js.css import videojs-contrib-hls const player videojs(this.$refs.videoPlayer, { autoplay: false, controls: true, sources: [{ src: this.videoUrl, type: application/x-mpegURL }] })m3u8视频流的来源通常是物业部署的流媒体服务器后端接口只需要返回视频流的地址给前端。前端封装一个PlayerModal.vue组件在公告详情里点击播放即可。这个能力扩展起来非常快复用性好以后如果做业主端小程序同样可以复用这个流媒体地址。6.3 员工操作日志与安全审计系统上线之后我还加了一个容易被忽略但非常重要的功能操作日志。不管是管理员删除了业主信息还是物业人员修改了账单金额系统都会记录下操作人、操作时间、操作类型和操作前后的数据变化。这里的实现有两种思路一种是针对核心业务代码手动记录日志一种是使用AOP切面统一记录。我选的是后者写了一个Log注解加在需要记录日志的Controller方法上配合切面自动把操作信息写入日志表。Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface Log { String module() default ; String action() default ; }AOP切面里在方法执行成功后读取注解信息获取当前登录用户把请求参数序列化成JSON存入t_oper_log表。这样出了问题可以回溯谁在什么时间干了什么一目了然。对于有合规需求的物业公司来说这个功能能避免很多纠纷。7. 写在最后的一些经验和心得这套SpringBootVueMySQL的小区物业管理系统从开发到上线我前后用了大概三周时间。第一周梳理业务和设计数据库第二周完成后端接口第三周集中开发前端页面并完成联调。整体难度不大但过程中有几个体会值得分享。第一数据库不要贪图一步到位。我建表之后陆续调整过好几次比如给t_owner加owner_type字段、给t_bill加paid_amount字段。这很正常的业务梳理只能在开发过程中逐步完善关键是预留好扩展空间别把字段写死。第二状态流转一定要在后端做校验不能只靠前端控制。有人可能会觉得这种小系统没那么严谨但一旦上线维修工误操作把工单从“待派单”直接点到“已完成”数据就乱了。状态机代码写起来很简单成本低收益高值得重视。第三前端不要写重复的逻辑。Element Plus的表格、表单场景非常固定把这套CRUD页面的公共逻辑抽出来做成配置化的组件后面每接一个新模块半小时就能生成一个页面。最后一个小技巧记得给后端接口统一加一个/api前缀然后在前端Nginx配置里做反向代理把/api开头的请求转发到Java服务。这样生产环境前端静态页面和后端接口都在同一个端口下不会跨域部署最简单。这套系统的意义不在于技术多高深而在于把一套真实的业务场景用主流技术栈完整地落地了。如果你正在找项目练手或者公司刚好有类似需求按照这篇文章的思路走一遍收获会非常大。
返回列表