尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SpringBoot+Vue+MyBatis+MySQL公寓报修系统源码解析

SpringBoot+Vue+MyBatis+MySQL公寓报修系统源码解析 先聊结论这套“企业级公寓报修管理系统”要是只看标题很多人第一反应是“又一个CRUD项目”但我实际把完整源码过了一遍之后发现它里面对报修流程、角色权限、状态机流转和前后端协作的处理远比大多数培训班项目要成熟。标题里的SpringBootVueMyBatisMySQL不是噱头而是非常务实的组合SpringBoot负责快速构建后端服务Vue撑起前端交互MyBatis把SQL自由度拉满MySQL做持久化。如果你正打算做一套带完整业务闭环的管理系统或者想找一个练手源码来研究企业级项目的标准分层这篇文章会把它的设计思路、落地细节和坑全部拆给你看。1. 系统整体设计与需求拆解1.1 公寓报修场景的痛点咱们先别急着看代码先把业务想明白。公寓报修跟普通网站后台不一样它的核心痛点有三个。第一个是信息断层。租客发现水龙头坏了传统的做法是打电话给前台前台手写登记然后维修工接到一张纸质单子。这个过程里“这个单子现在在谁手上”“修得怎么样了”“有没有超时没人处理”全是黑盒。租客催一次前台就要打电话问一次维修工维修工可能正在干活根本接不到电话。第二个是状态混乱。一面墙漏水可能是从楼上漏下来的一个工单可能要转给两个维修工处理到一半发现需要采购配件这时候单子是算“处理中”还是“挂起”要是不设计清楚后台统计的数据全是错的月底对账对不上。第三个是数据缺失。今天修了什么、换了什么零件、哪一类报修最多、哪个维修工效率最高这些数据如果全靠纸质记录根本没法分析。而公寓管理方恰恰最需要这些数据因为报修频率直接跟房屋维护成本和住户满意度挂钩。这套源码解决的就是这三个问题。它把报修从“人工催办”变成“系统流转”租客在线提交系统自动派单给维修工维修工接单、处理、反馈租客评价全程留痕可追溯。数据沉淀下来之后运营方还能看到各类报修的分布和每个人的工作量。1.2 角色与核心流程设计系统里设计了三类核心角色这一点很关键因为它决定了权限模型和页面菜单的划分。租客住户提交报修、查看处理进度、对已完成工单进行评价。维修工处理人接单、拒单/转单、填写处理结果、申请挂起。管理员公寓运营方创建用户、派单/改派、审核工单、查看统计报表、管理维修物料。如果你拿到的源码版本把管理员又拆成了“前台客服”和“超级管理员”那更说明它走的是真实企业路线前台负责日常派单超管负责系统配置和数据导出。核心流程我梳理一下也是后面状态机设计的依据租客提交报单填写房号、问题描述、照片可选。系统生成待处理工单状态为“待派单”。管理员受理选择维修工进行派单状态变为“待接单”。维修工接单状态变为“处理中”。维修工处理完成填写维修记录状态变成“待验收/已完成”。租客确认并评价流程结束如果要走验收环节管理员确认后工单才关闭。看明白这个流程你就知道后面所有表结构和接口设计其实都是围绕这条业务线展开的。1.3 技术栈选定不只是“火”那么简单这套系统的技术栈是SpringBoot Vue MyBatis MySQL可能有人觉得“太常规了”。但常规不意味着平庸关键在于这组技术组合在公寓报修这类中后台业务里是性价比最高的选择。SpringBoot的价值在于“开箱即用”。报修系统需要用户认证、接口鉴权、文件上传、定时任务这些基础设施SpringBoot用starter就能飞快集成不需要像早期SSH那样写一大堆XML配置。同时它的Tomcat内嵌机制让部署变得非常简单打一个jar包丢到服务器就能跑。Vue作为前端框架核心优势是组件化和响应式。报修系统里有大量表单、列表、状态标签切换的交互用Vue的v-model、计算属性和组件通信就能非常清晰地组织代码。2.x还是3.x都无所谓重点是逻辑清晰就行。MyBatis在这个项目里的作用经常被低估。报修系统的查询条件特别灵活按状态查、按时间范围查、按房号查、按维修工查还要做分组统计这些靠JPA自动生成的SQL很难写好但MyBatis的动态SQL可以精确控制每一条查询语句。标题里特意点出MyBatis说明这不是那种用Spring Data JPA糊弄过去的学生项目。MySQL则是稳定兜底。报修系统的事务要求不高但并发写入量在峰值时也不算小MySQL的InnoDB引擎配合合理索引完全能支撑几百栋公寓同时在线报修。很多企业不敢上PostgreSQL或Oracle不是因为它们不好而是团队维护成本高。MySQL的生态和运维经验是最大优势。提示你拿到源码后第一步不要急着改功能先看pom.xml和前端package.json里锁定的版本。SpringBoot 2.x和3.x在javax到jakarta命名空间上有大改动Vue 2和Vue 3的写法也完全不同版本错了后面全是问题。1.4 系统模块清单看完源码我先帮你把模块归个类方便后面逐块拆解用户认证模块登录、退出、Token刷新、验证码有些版本含。报修工单模块提交、派单、接单、处理、评价全流程。用户管理模块租客、维修工、管理员账号的增删改查和状态禁用。通知消息模块关键节点给相关人发送站内消息或短信。统计报表模块按楼栋、按问题类型、按维修工统计工单数量和完成率。基础配置模块维修类型字典、故障等级、楼栋房号维护。这些模块几乎覆盖了一个“小而全”的企业级业务系统所有标准组成。你要是想拿这套源码去面试或写论文按这个模块清单逐项讲远比只讲“登录和增删改查”有说服力。2. 数据库设计与核心表结构看任何一个管理系统源码我先看数据库脚本因为表结构直接决定业务能做多深。这套源码的表设计整体是中规中矩的“范式化适度冗余”既不会复杂到没法维护也不会简单到业务推不开。2.1 用户与权限模型用户部分一般会拆成用户表sys_user和角色表sys_role通过中间表关联。注意报修系统里的用户不是单纯的“系统用户”它要承载租客信息所以往往还会扩展租客专属字段。我当时看到源码里用户表包含这些关键字段id主键建议雪花ID或自增ID看个人习惯。username、password登录凭证密码必须是BCrypt加密存储。real_name、phone真实姓名和手机号报修联系要用。role_id角色ID简单方案就直接挂一个角色字段。community_id、building_id、room_no社区、楼栋、房号这些是公寓业务的必备维度。status账号状态1启用0禁用。权限方面如果做的是极简版那登录后返回角色编码前端根据角色控制菜单和按钮如果做的是企业版会引入Spring Security的RBAC体系后端用PreAuthorize注解做接口级授权。从实用性上讲极简版够用但企业版更能体现源码含金量。注意密码千万不要用MD5明文存储。哪怕是demo项目也请用BCrypt或至少加盐哈希。公寓系统的用户信息安全虽然不像金融那么敏感但一旦发生撞库事件责任同样是你的。2.2 报修工单主表设计这是整套系统的核心表。我把它单独拿出来讲因为字段设计能直接反映作者对需求的理解程度。工单表repair_order的核心字段通常是id主键。order_no工单编号业务唯一常用于客服查单格式类似“BX 年月日 序列号”。user_id提交人即租客。room_full_name冗余的房号全称比如“A栋3单元502”查单方便不用联表查房号。category_id故障类型ID关联字典表比如漏水、电路、门锁。level故障等级普通/紧急影响派单优先级。description问题描述。images图片地址列表一般存多个URL用逗号分隔或者JSON字符串。status工单状态核心状态字段。assign_user_id指派的维修工。create_time、assign_time、finish_time各节点时间非常关键用来算响应时长和完成时长。remark内部备注租客不可见。deleted逻辑删除标记企业项目必备。我特别欣赏这套工单表的地方在于它把“异常状态”也考虑进去了。有些工单会因为配件缺失被挂起如果表里没有is_suspended、suspend_reason这类字段想要做挂起功能就只能硬编码在别的表里最后查数据一塌糊涂。索引方面也别忽视。order_no要建唯一索引status、assign_user_id、create_time要建联合索引因为业务上最常出现的查询是“某个维修工在某个时间范围内的所有工单”。如果没建索引数据量一旦过万查询就会开始明显变慢。2.3 处理记录与评价表报修工单是“主文档”但每一次操作派单、接单、反馈、评价都需要留痕。于是就有了工单流转记录表repair_trace和评价表repair_evaluation。流转记录表的字段设计逻辑是“流水账”id主键。order_id工单ID。operator_id操作人ID。action操作动作比如assign/accept/finish。from_status、to_status操作前后的状态这个设计能直接回溯状态机流转路径。content操作内容比如“派给张三”“已更换水管接头”。create_time操作时间。一套完整的流转记录特别有用。比如租客投诉说“根本没人来修过”管理员拉出流转记录一看其实维修工早就接单了单子卡在“处理中”没推进责任就非常清晰。评价表其实可以合并到主表里但拆出来更好扩展id、order_id。user_id评价人。score评分1到5星。content评价内容。create_time评价时间。anonymous是否匿名。我建议你实际使用这套源码时把“评价表”创建时间和工单的finish_time做一次商品校验如果评价时间在完成时间之前那说明前端接口被绕过或者数据有问题。这个检查写成一个SQL就能定期跑能抓出不少脏数据。2.4 状态机报修单的一生网上许多项目的工单状态都是硬编码每个接口里都能改状态最后状态变得不可控。但这个项目让我比较满意的地方是它把状态流转集中处理形成一个清晰的状态机。标准流转可以总结为待派单 → 待接单 → 处理中 → 已完成 → 已评价关闭中间允许出现分支待接单 →拒单→ 待派单 处理中 →挂起→ 挂起 →恢复→ 处理中 处理中 →转交→ 待接单这种设计本质上是把每一张工单当成一个状态对象只有符合规则的动作才能推进状态。你在源码里如果看到状态变更的逻辑集中在Service层甚至单独一个StateMachine类里那说明作者是真的懂“防止乱改状态”的重要性。3. 后端核心功能实现拆解3.1 工程结构与分层源码拿到手先看目录结构。标准的企业级SpringBoot工程基本是这么分的controller接口层只负责接收参数和返回结果。service业务层事务、业务逻辑都在这层。mapper/dao数据访问层对应MyBatis的Mapper接口。entity/domain实体类。dto/vo接收前端参数和返回前端展示的数据对象。config配置类比如跨域、拦截器、MyBatis配置。common/utils公共工具类、统一返回结果封装。这套分层核心思想是“单向依赖”controller依赖serviceservice依赖mapperentity可以被所有层引用但上层绝不对下层藏着掖着。我见过不少半吊子工程把业务SQL直接写在controller里看起来功能能跑但过了两个月想加一个字段到处都是魔法数字改起来想死。3.2 登录认证JWT方案报修系统肯定要登录。大部分完整源码都会选JWTJSON Web Token而不是传统Session原因很简单后端项目如果拆了几台实例Session就要做共享复制而JWT是无状态的后端只需要验签就行。具体到代码层面JWT在登录流程里一般是这么运作的用户提交用户名密码。后端校验密码BCrypt.matches。校验通过后生成tokenpayload里放userId、username、role。token返回前端前端存在localStorage或vuex里。后续请求在请求头带Authorization: Bearer token。后端拦截器从token里解析出用户信息塞进ThreadLocal。上游Service从ThreadLocal里取当前操作人ID用来记录“谁做了什么”。写代码时有个关键坑ThreadLocal记得在请求结束后清理否则线程池复用会导致下一条请求拿到上一条的用户信息轻则日志错乱重则越权操作。这块源码里如果用了拦截器通常在afterCompletion方法里做remove。权限上后端的角色控制往往是硬性的。我建议你重点看一眼源码里的AdminInterceptor或者SecurityConfig正常版本应该会对“派单接口”“统计接口”做管理员角色校验如果只是登录就能调派单接口那源码的鲁棒性就要打个问号。注意JWT有一个默认的坑就是“无法主动注销”。如果你的源码里没有把token版本号存进Redis并在修改密码时递增那么旧token在过期前一直有效。公寓管理员的账号如果被离职员工拿着旧token就能继续调接口这在企业内是很严重的安全隐患。建议你实际部署时把JWT的secret配置成64位以上随机串并且定期轮换。3.3 报修单创建接口从请求到落库用户提交报修这个接口值得仔细讲因为它是整个系统的入口做得不好后面全乱。我先给你梳理标准流程对照源码看会更清楚controller接收RepairCreateDTO包含描述、照片URL、故障类型、联系电话。service里先校验当前用户是否绑定有效房号防止乱填。生成order_no可以用“日期 随机数”或者数据库序列。注意并发重复问题唯一索引兜底。插入主表repair_order状态置为待派单。在repair_trace表插入一条“创建工单”记录。如果配置了通知模块向管理员推送“新报修待派单”消息。这里我重点提醒一个容易被忽略的细节图片上传。前端的图片一般是先通过独立的上传接口传到服务器或OSS获得URL之后再随报修单一起提交。不少初级项目会把图片直接base64塞进数据库这种做法的后果是数据库里存几MB的字符串查询性能直接垮掉。事务方面创建报修单这个方法必须加Transactional。因为主表插入和记录表插入必须同生共死如果只成功一半工单状态和操作日志就对不上。3.4 MyBatis动态SQL与统计报表报修系统的查询条件非常灵活这套源码用MyBatis动态SQL处理得相当漂亮。查看工单列表往往需要动态拼接条件源码里常见的写法是select idselectRepairList resultTypecom.xxx.vo.RepairVO select * from repair_order where if teststatus ! null and status ! and status #{status} /if if testassignUserId ! null and assign_user_id #{assignUserId} /if if testbeginTime ! null and create_time gt; #{beginTime} /if if testendTime ! null and create_time lt; #{endTime} /if if testcategoryId ! null and category_id #{categoryId} /if /where order by create_time desc /select标签的妙处在于MyBatis会智能处理多余and不用自己手工拼WHERE 11这种丑代码。这种写法看起来简单实际维护起来非常香新增查询条件时只需加一个 块。统计报表是报表模块的重头戏。常见需求是“按故障类型统计数量”和“按维修工统计完成率”。如果源码里有这类统计SQL通常长这样select category_id, count(*) as cnt from repair_order where create_time between #{begin} and #{end} group by category_id order by cnt desc这类SQL基本没法用JPA自动生成而且优化空间很大。数据量大之后可以考虑在group by之前加create_time索引或者提前汇总到统计表。4. 前端Vue实战要点4.1 工程结构与路由守卫前端的工程结构如果是Vue CLI或Vite创建的标准项目核心目录通常是这样src/api接口请求封装。src/router路由配置。src/storeVuex/Pinia全局状态。src/views页面组件按模块分文件夹。src/components公共组件。src/utilsaxios封装、工具函数。路由守卫几乎必然会用到因为要控制“未登录不能访问”和“角色权限控制”。标准写法大概长这样router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (!token to.path ! /login) { next(/login) } else if (token to.meta.roles !to.meta.roles.includes(store.getters.role)) { next(/403) } else { next() } })这里有个实操重点前端路由守卫只是用户体验层面的控制不是安全措施。接口权限必须后端再验证一遍否则别人直接拿token调接口前端挡不住的。我在很多项目里见到表单按钮隐藏了就以为安全了这是大忌。4.2 核心页面提交报修与工单处理提交报修页面是这个系统前端最值得你细看的页面。它不仅仅是一个表单更是一个“业务表达”。好的实现至少包含这些交互状态故障类型下拉框数据从字典接口实时拉取。故障等级选择紧急级别会置顶显示红色警示。图片上传组件支持压缩预览、删除重传。房号回显当前登录用户自带的楼栋房间信息只读展示。提交成功后的打气页面给租客一个“我们的维修工很快联系您”的反馈。工单处理页面维修工视角则要简洁高效。核心功能就是“待接单”和“进行中”两个Tab处理按钮只有“接单”“完成”两个主操作辅助操作是“转单”“申请挂起”。按钮越多用户越懵能把核心操作做得聚焦说明作者懂B端交互。前端写组件时有个经验所有请求走统一的api模块不要在页面里new XMLHttpRequest或者到处写axios。统一封装之后可以集中处理错误码、Token过期、Loading状态。4.3 Token管理与文件上传前端axios拦截器是必须要有的一个环节。响应拦截器里做了什么直接决定系统是否健壮。标准的做法是请求拦截器从localStorage取token放到header。响应拦截器判断HTTP状态码和业务code如果业务code是401说明token失效清掉本地token强制跳到登录页。其他错误码统一弹出message提示不让用户看到满屏英文异常。文件上传这块前端要注意用FormData封装文件然后向后端/upload接口提交。这里有个常见坑上传接口一般不需要带业务参数但需要登录鉴权所以拦截器要排除掉某些请求路径否则上传被拦。提示如果你部署后上传图片一直失败先别急着看业务代码打开浏览器F12看上传请求的网络面板。是CORS报错、请求头认证失败还是文件太大被Nginx的client_max_body_size限制定位到的原因几乎都不一样。5. 部署运行与关键配置5.1 本地联调准备拿到源码第一件事不是直接编译运行而是先看README确认依赖环境版本。如果源码里明确写了SpringBoot 2.7.x那要求的JDK大概率是8或11如果写着SpringBoot 3.x那必须JDK 17以上。前端Node版本同理Vue2项目用Node 16问题不大Vue3Vite项目建议Node 18。数据库准备建库语句和执行初始SQLinit.sql这个文件一般包含建表语句和初始管理员账号。执行完SQL之后立刻去数据库里看看表结构能否和实体类对上很多下载的源码表结构和代码不一致典型原因是SQL版本没更新。后端启动前必须改三样东西application.yml里的数据库账号密码。JWT自定义配置里的密钥非常重要别用默认值。文件上传保存路径别直接放在C盘根目录。后端起来之后端口通常8080。先用Swagger或直接浏览器访问/api/login试试接口通不通确认后端没问题再启动前端。前端启动就是常规流程npm install npm run serve不过npm install经常有一段揪心的等待期。建议切到淘宝镜像源能省下大量时间。如果install过程中出现node-sass报错那基本是Node版本和node-sass版本不匹配解决方案是用Python编译源码或直接换成sassdart-sass。5.2 前后端打包与部署本地跑通之后部署到服务器是另一回事。后端打包很简单mvn clean package -DskipTests打出来的jar包放在服务器上用systemd或直接用java -jar启动就行。注意生产环境的内存参数java -Xms256m -Xmx512m -jar repair-system.jar --spring.profiles.activeprod不要上来就-Xmx2g报修系统这种量级的业务256到512兆足够留内存给其他进程。前端打包npm run build打包后的dist目录里有静态文件可以用Nginx部署。Nginx配置里除了常规的root和index还有两个地方特别重要。第一个是处理history路由location / { try_files $uri $uri/ /index.html; }第二个是反向代理后端接口location /api/ { proxy_pass http://127.0.0.1:8080; }注意前端打包之后如果没有处理跨域直接把dist和jar分开部署浏览器访问前端域名时会遇到跨域问题。最简单的方案是让Nginx把/api开头的请求都代理到后端服务前端代码里请求路径就写相对路径不需要写绝对域名。5.3 MySQL部署与数据备份数据库部署建议单独一台机器至少也是单独一个实例。生产环境MySQL要做的几件事这套系统跑起来之后一定用得着开启binlog方便做数据恢复和主从同步。定期全量备份用mysqldump配crontab凌晨执行。设置慢查询日志把超过1秒的SQL捞出来优化。MySQL安装时容易踩的坑一是字符集没有设置为utf8mb4导致表情符号和生僻字乱码二是时区不对程序里插入的时间跟数据库差了8小时。这两个问题都必须在配置阶段解决不然后面改配置代价很大。6. 踩坑日志与排查技巧6.1 跨域问题这个坑几乎每个前后端分离项目都会遇到。后端明明启动了前端一调接口就报Access to XMLHttpRequest at http://localhost:8080/api/... from origin http://localhost:8081 has been blocked by CORS policy解决方式有两种。第一种在后端添加全局跨域配置Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(Registry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET,POST,PUT,DELETE,OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }第二种是生产环境用Nginx反向代理从根源上让前端请求变成同源。我实际排查时建议先看浏览器的请求如果是OPTIONS请求都返回失败优先怀疑CORS如果OPTIONS成功但POST失败那要检查请求头或者后端拦截器有没有处理Preflight请求。6.2 事务失效与状态流转Service方法加Transactional注解但运行时报错数据却没回滚这种问题排查起来比较隐蔽。常见的失效场景有三个同类内部调用B方法加事务但A方法没加A调用B时this调用事务不生效。catch了异常但不重新抛出事务管理器看不到异常当然不会回滚。数据库表用了MyISAM引擎这种老引擎不支持事务。修状态时另一个高发问题是状态跳变。比如待接单的工单被直接调接口改成已完成绕过了所有校验。治理方案就是状态机校验Service层定义一个枚举或者map来规定合法流转路径不合法的直接抛业务异常。源码要是没做你改造时第一优先就是补这块。6.3 MyBatis高频问题速查MyBatis是这套系统的持久层核心我用过这么多年把最容易踩的坑整理了一下你对照排查症状原因解决办法Invalid bound statement (not found)Mapper接口和XML没扫描到启动类加MapperScanXML放在resources/mapper目录查询结果全是null数据库字段下划线和实体类驼峰没映射mybatis.configuration.map-underscore-to-camel-casetrue局部变量显示Parameter xxx not foundMapper接口方法参数没加Param多个参数必须显式加Param注解SQL有保留字导致建表失败字段名叫desc、level等建表SQL用反引号包住字段名大数据量查询慢没索引或索引没走对explain执行计划检查where条件列是否建索引报修工单表的order_no查询频率很高如果你发现按单号查询很慢大概率是没建唯一索引如果status和assign_user_id的联合查询慢就考虑建联合索引而不是单列索引。6.4 图片上传成功后页面不显示这个现象也常见。明明上传返回的URL能访问但工单列表里图片不显示。我遇到过的原因多半是后端返回的是相对路径比如/upload/20240901/xxx.jpg而工单列表的页面是在8081端口跑的浏览器拿着8081的域名去拼这个相对路径自然就404了。解决办法是前端拼接完整地址把图片服务器的域名或Nginx代理路径拼上。6.5 定时任务跑不起来报修系统如果需要做“超时未处理自动升级”或者“每日统计报表”一般会用到Scheduled。我在调试这类任务时踩过的坑是定时任务所在的类没有被Spring扫描到或者单测环境里没有启用EnableScheduling。排查思路其实很简单在任务方法第一行加日志如果日志没打出来那就说明根本没有触发优先检查启动类有没有加EnableScheduling注解。这套源码还能怎么用聊到这儿其实整套系统的主干已经被拆得差不多了。我个人在实际操作中最大的体会是源码这东西单纯跑起来就算完是最没价值的你要把它当成一个“业务骨架”去理解每条业务线背后的流程设计。拿这个项目来说你可以在它基础上往三个方向扩展价值立刻翻倍。第一个方向是接入消息推送。现在很多工单状态的更新租客根本不知道你可以接一个微信模板消息或者钉钉机器人通知工单一有变化就推给相关人。第二个方向是加一个维修物料库存模块维修工处理时选择使用了哪些配件库存自动扣减月底对账再也不用手工盘点。第三个方向是把统计报表改成实时大屏用定时任务把前一天的工单数据汇进一张中间表前端每分钟拉一次大屏上滚动展示各类维修趋势。最后分享一个我最初调试这套代码时用的笨办法每张工单从提交到关闭我都在数据库里手动刷一条状态记录把时间点记下来然后跟前端页面显示的状态变化对比。这个方法虽然土却能最快把那几个状态流转的逻辑彻底吃透。真正理解状态机之后这套系统的设计精髓也就被你拿走了。
返回列表