ARTICLE DETAIL

资讯详情

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

SpringBoot+Vue社区维修平台毕设实战指南

SpringBoot+Vue社区维修平台毕设实战指南 简介本资源是一套面向计算机专业本科生的毕业设计实战项目聚焦社区维修服务场景采用Spring Boot Vue.js前后端分离架构解决传统社区报修流程低效、信息不透明等实际问题适合课设、毕设及期末大作业参考与二次开发。压缩包含808个文件总大小23.76MB涵盖122个Java后端核心类、46个Vue组件、153个JS交互逻辑、44个CSS样式文件、48个JPG/GIF图片资源及1个完整MySQL建库脚本.sql另有BreadCrumbs.vue.bak等调试备份文件和2-run.bat等部署批处理脚本体现真实开发迭代痕迹。已有162人学习下载资源提供可直接运行的源码、配套数据库、详细项目说明文档及参考论文目录结构规范模块划分清晰含用户管理、工单调度、维修进度跟踪等核心功能支持在JDKIDEATomcat环境下快速部署验证。1. 为什么社区维修平台成了Java毕业设计的“高频翻车现场”SpringBoot Vue组合不是搭积木而是修水管你手里的毕设题目写着“基于SpringBoot Vue的社区维修平台”但真正打开代码仓库时八成会看到前端路由跳转404、后端接口返回500却没日志、用户提交报修单后数据库里空空如也、Vue打包后静态资源404、管理员登录成功却进不了后台——这不是你代码写得差而是这个组合在毕业设计场景下天然带着三重“隐性摩擦”前后端分离带来的路径错位、开发环境与生产部署的配置断层、业务逻辑简单但状态流转密集导致的边界漏判。我带过17届毕设统计过近3年校内答辩挂科项目62%的失败案例都卡在“能跑通Hello World但跑不通一个报修单提交流程”。这篇笔记不讲SpringBoot启动原理也不教Vue响应式源码只聚焦一件事用最小可行路径把“居民在线报修→物业接单→师傅上门→评价闭环”这根业务水管从设计图拧进真实可运行的jar包dist目录里且让答辩老师点开就能走完全流程。适合正在赶毕设 deadline 的本科生、需要快速验证业务模型的实训学员以及想用真实小系统练手 SpringBoot Vue 工程化能力的转行者。文中所有命令、配置、SQL、接口定义均来自我去年帮3个学院学生落地的6个同类型项目已剔除所有IDE特定插件依赖、非标构建工具和本地调试代理。2. 搭建骨架用SpringBoot 2.7.18 Vue 2.6.14构建可交付的最小闭环提示别碰SpringBoot 3.xJDK17和Vue 3Composition API毕设答辩环境普遍停留在JDK8/11 Vue2生态强行升级只会增加不可控变量。本文所有版本号经实测兼容主流教学机房环境Windows 10 IDEA 2022.3 Chrome 114。2.1 后端SpringBoot工程结构必须砍掉“炫技模块”只留四根支柱毕业设计不是企业级中台SpringBoot项目结构必须做减法。删掉spring-boot-starter-webflux、spring-boot-starter-aop除非真要用日志切面、spring-boot-devtools答辩演示时禁用热部署更稳定。核心依赖仅保留!-- pom.xml 关键依赖 -- dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId version2.7.18/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-jdbc/artifactId version2.7.18/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope version8.0.33/version /dependency dependency groupIdcom.alibaba/groupId artifactIddruid-spring-boot-starter/artifactId version1.2.18/version /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies为什么选Druid而非Hikari教学机房MySQL常开在虚拟机或老旧物理机上Druid的监控页面/druid/login.html能直观看到连接池活跃数、SQL执行耗时答辩时老师问“你怎么知道数据库没瓶颈”直接截图比讲理论管用。Hikari虽快但无内置监控页临时加Actuator又增复杂度。关键配置项application.yml必须显式声明server: port: 8080 servlet: context-path: /api # 所有后端接口统一前缀避免Vue路由冲突 spring: datasource: druid: url: jdbc:mysql://localhost:3306/community_repair?useUnicodetruecharacterEncodingUTF-8serverTimezoneAsia/Shanghai username: root password: 123456 initial-size: 5 max-active: 20 min-idle: 5 stat-view-servlet: enabled: true login-username: admin login-password: admin123 servlet: context-path: / # SpringBoot应用根路径与Vue静态资源部署路径对齐 mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true注意server.servlet.context-path: /api和spring.servlet.context-path: /的组合是解决“Vue history模式路由404”的核心——后端只接管/api/**路径其余全部放行给前端静态资源处理。这点在答辩PPT里务必画图说明否则老师会质疑“为什么接口要加/api前缀”。2.2 前端Vue 2.6.14脚手架必须锁定依赖禁用动态特性用vue-cli 3.12.2创建项目vue create community-repair-vue创建时取消Router、Vuex、CSS Pre-processors所有选项手动安装最简依赖npm install vue-router3.6.5 axios0.21.4 element-ui2.15.14 --save npm install babel-plugin-component1.1.1 --save-dev为什么不用Vue CLI 4/5部分高校机房Node.js版本卡在14.xVue CLI 4要求Node 16安装失败率超40%。vue-cli 3.12.2是Node 12兼容的最后稳定版且生成的webpack配置更透明便于修改vue.config.js。关键配置vue.config.js必须覆盖三处// vue.config.js const path require(path) module.exports { // 1. 静态资源输出路径必须与SpringBoot static目录对齐 outputDir: path.resolve(__dirname, ../backend/src/main/resources/static), // 2. 开发代理指向SpringBoot后端仅dev环境 devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true, pathRewrite: { ^/api: } } } }, // 3. 生产环境关闭sourceMap减少dist体积答辩演示加载更快 productionSourceMap: false }为什么outputDir指向../backend/src/main/resources/static这是SpringBoot默认静态资源路径。Vue打包后所有文件index.html、js、css自动落入该目录SpringBoot启动时无需额外配置即可托管。避免常见错误把dist扔进src/main/webappSpringBoot 2.x已弃用或resources/public需额外配置spring.web.resources.static-locations。2.3 数据库用5张表撑起维修业务拒绝过度设计社区维修平台核心是“人-事-物”流转5张表足够支撑完整流程含答辩演示所需全部功能表名字段关键说明userid, username, password, role(resident,staff,admin), phone用户基础信息role字段控制权限repair_orderid, user_id, title, description, address, status(pending,assigned,completed,closed), create_time报修单主表status用字符串枚举更易调试repair_assignmentid, order_id, staff_id, assign_time, complete_time派单记录表解耦派单与完成动作repair_imageid, order_id, image_url, upload_time报修图片表支持多图上传repair_commentid, order_id, user_id, content, create_time评价表关联报修单和用户建表SQLMySQL 5.7必须包含-- 创建repair_order表含中文注释答辩时老师会看 CREATE TABLE repair_order ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键ID, user_id bigint(20) NOT NULL COMMENT 报修人ID, title varchar(100) NOT NULL COMMENT 报修标题, description text COMMENT 详细描述, address varchar(200) NOT NULL COMMENT 具体地址, status varchar(20) DEFAULT pending COMMENT 状态pending待处理/assigned已派单/completed已完成/closed已关闭, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT报修单主表;注意status字段用varchar(20)而非tinyint答辩演示时直接在MySQL客户端执行UPDATE repair_order SET statuscompleted WHERE id1;老师能肉眼看到状态变更比查日志或调接口更直观。3. 串起业务从居民报修到师傅评价写透三个核心接口的实现逻辑毕业设计答辩最常被问“这个功能怎么实现的”——不是问架构图而是问一行代码到一行SQL之间发生了什么。以下三个接口覆盖90%答辩提问场景全部给出可直接复制的代码关键注释。3.1 居民提交报修单Controller → Service → Mapper三层拆解Controller层RepairOrderController.javaRestController RequestMapping(/api/repair) public class RepairOrderController { Autowired private RepairOrderService repairOrderService; /** * 居民提交报修单 * POST /api/repair/orders * 请求体{ title: 水龙头漏水, description: 厨房水槽下方滴水, address: 3栋2单元501 } * 成功返回{ code: 200, data: { id: 101 } } */ PostMapping(/orders) public ResultVO submitOrder(RequestBody RepairOrderDTO dto, RequestHeader(X-User-ID) Long userId) { // 1. 校验必填字段答辩时老师会故意传空title测试 if (StringUtils.isBlank(dto.getTitle()) || StringUtils.isBlank(dto.getAddress())) { return ResultVO.fail(标题和地址不能为空); } // 2. 构建实体DTO转Entity此处省略BeanUtils.copyProperties RepairOrder order new RepairOrder(); order.setUserId(userId); order.setTitle(dto.getTitle()); order.setDescription(dto.getDescription()); order.setAddress(dto.getAddress()); order.setStatus(pending); // 初始状态为待处理 // 3. 调用Service重点事务控制在此层 Long orderId repairOrderService.createOrder(order); return ResultVO.success(Collections.singletonMap(id, orderId)); } }Service层RepairOrderServiceImpl.javaService Transactional(rollbackFor Exception.class) // 必须加事务否则插入失败不回滚 public class RepairOrderServiceImpl implements RepairOrderService { Autowired private RepairOrderMapper repairOrderMapper; Override public Long createOrder(RepairOrder order) { // 1. 插入主表自增ID repairOrderMapper.insert(order); // 2. 记录操作日志答辩加分项体现工程规范 log.info(居民[{}]提交报修单[{}], 地址:[{}], order.getUserId(), order.getId(), order.getAddress()); return order.getId(); // 返回主键ID供前端跳转 } }Mapper层RepairOrderMapper.xml!-- mapper/RepairOrderMapper.xml -- insert idinsert parameterTypeRepairOrder useGeneratedKeystrue keyPropertyid INSERT INTO repair_order ( user_id, title, description, address, status, create_time ) VALUES ( #{userId}, #{title}, #{description}, #{address}, #{status}, NOW() ) /insert关键点说明RequestHeader(X-User-ID)用于获取JWT或Session中的用户ID避免前端传恶意user_id答辩时可演示用Postman设置Header发送请求。useGeneratedKeystrue确保MyBatis自动回填自增主键order.getId()才能拿到真实ID。log.info日志必须存在答辩老师问“你怎么确认数据写入成功”直接打开logs/spring.log定位到这条日志。3.2 物业派单给师傅状态机驱动的原子更新Controller层RepairAssignmentController.javaRestController RequestMapping(/api/repair) public class RepairAssignmentController { Autowired private RepairAssignmentService assignmentService; /** * 物业管理员派单仅roleadmin可调用 * PUT /api/repair/orders/{orderId}/assign * 请求体{ staffId: 1001 } * 成功返回{ code: 200, msg: 派单成功 } */ PutMapping(/orders/{orderId}/assign) public ResultVO assignToStaff(PathVariable Long orderId, RequestBody AssignDTO dto, RequestHeader(X-Role) String role) { // 1. 权限校验答辩必问怎么防止居民调这个接口 if (!admin.equals(role)) { return ResultVO.fail(无权执行派单操作); } // 2. 状态校验只能给pending状态的单派单 RepairOrder order repairOrderMapper.selectById(orderId); if (!pending.equals(order.getStatus())) { return ResultVO.fail(该报修单状态不可派单当前状态 order.getStatus() ); } // 3. 执行派单Service层保证原子性 assignmentService.assignOrder(orderId, dto.getStaffId()); return ResultVO.success(派单成功); } }Service层RepairAssignmentServiceImpl.javaService Transactional(rollbackFor Exception.class) public class RepairAssignmentServiceImpl implements RepairAssignmentService { Autowired private RepairOrderMapper repairOrderMapper; Autowired private RepairAssignmentMapper assignmentMapper; Override public void assignOrder(Long orderId, Long staffId) { // 1. 更新报修单状态为assigned乐观锁防并发 int updated repairOrderMapper.updateStatusById( orderId, pending, assigned); // 只有原状态是pending才更新 if (updated 0) { throw new RuntimeException(派单失败报修单状态已变更); } // 2. 插入派单记录 RepairAssignment assignment new RepairAssignment(); assignment.setOrderId(orderId); assignment.setStaffId(staffId); assignment.setAssignTime(new Date()); assignmentMapper.insert(assignment); } }Mapper层RepairOrderMapper.xml!-- 乐观锁更新状态确保状态未被其他线程修改 -- update idupdateStatusById parameterTypemap UPDATE repair_order SET status #{newStatus}, update_time NOW() WHERE id #{orderId} AND status #{oldStatus} /update血泪经验答辩时老师会模拟并发场景——两个管理员同时点同一个报修单的“派单”按钮。用WHERE status pending做条件更新比先查再更新Read-Modify-Write更可靠且MyBatis返回updated行数可直接判断是否成功。3.3 师傅完成维修并上传凭证文件上传状态联动Controller层RepairImageController.javaRestController RequestMapping(/api/repair) public class RepairImageController { Autowired private RepairImageService imageService; /** * 师傅上传维修凭证图片单次最多3张 * POST /api/repair/orders/{orderId}/images * 文件字段名imagesMultipartFile数组 * 成功返回{ code: 200, data: [ http://localhost:8080/images/abc.jpg ] } */ PostMapping(/orders/{orderId}/images) public ResultVO uploadImages(PathVariable Long orderId, RequestParam(images) MultipartFile[] files, RequestHeader(X-Role) String role) { // 1. 角色校验仅staff可上传 if (!staff.equals(role)) { return ResultVO.fail(仅维修师傅可上传凭证); } // 2. 文件校验答辩演示时故意传超大文件测试 if (files.length 0) { return ResultVO.fail(请至少上传1张图片); } if (files.length 3) { return ResultVO.fail(最多上传3张图片); } for (MultipartFile file : files) { if (file.getSize() 5 * 1024 * 1024) { // 5MB限制 return ResultVO.fail(单张图片不能超过5MB); } } // 3. 执行上传Service层处理存储和DB写入 ListString urls imageService.uploadImages(orderId, files); return ResultVO.success(urls); } }Service层RepairImageServiceImpl.javaService Transactional(rollbackFor Exception.class) public class RepairImageServiceImpl implements RepairImageService { Value(classpath:static/images/) // SpringBoot静态资源路径 private Resource imagesDir; Override public ListString uploadImages(Long orderId, MultipartFile[] files) { ListString urls new ArrayList(); try { for (MultipartFile file : files) { // 1. 生成唯一文件名避免中文名乱码 String originalName file.getOriginalFilename(); String ext originalName.substring(originalName.lastIndexOf(.)); String fileName UUID.randomUUID().toString() ext; // 2. 保存到static/images目录SpringBoot自动托管 Path targetPath Paths.get(imagesDir.getFile().getAbsolutePath(), fileName); Files.createDirectories(targetPath.getParent()); file.transferTo(targetPath); // 3. 写入数据库 RepairImage image new RepairImage(); image.setOrderId(orderId); image.setImageUrl(/images/ fileName); // 前端访问URL image.setUploadTime(new Date()); repairImageMapper.insert(image); urls.add(/images/ fileName); } // 4. 所有图片上传成功后更新报修单状态为completed repairOrderMapper.updateStatusById(orderId, assigned, completed); } catch (IOException e) { log.error(图片上传失败, e); throw new RuntimeException(图片上传失败请重试); } return urls; } }关键细节Value(classpath:static/images/)直接定位到SpringBoot静态资源目录比用System.getProperty(user.dir)更可靠。image.setImageUrl(/images/ fileName)中的/images/是SpringBoot默认静态路径前缀前端img :srcurl可直接渲染。状态联动只有所有图片上传成功才更新订单状态避免“图片上传失败但状态已变”的脏数据。4. 避坑指南答辩前夜必须检查的5个致命陷阱毕业设计答辩前48小时90%的挂科源于这5个看似微小却致命的配置错误。以下按现象→原因→解决顺序列出每条均来自真实翻车案例。4.1 现象Vue页面刷新后路由404但点击菜单跳转正常原因Vue Router使用history模式而SpringBoot未配置fallback路由导致直接访问/admin时SpringBoot找不到对应静态资源返回404而非index.html。解决在SpringBoot中添加WebMvcConfigurer将所有非API请求重定向到index.htmlConfiguration public class WebConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/**) .addResourceLocations(classpath:/static/); } Override public void addViewControllers(ViewControllerRegistry registry) { // 将所有非/api路径映射到index.htmlVue Router接管 registry.addViewController(/).setViewName(forward:/index.html); registry.addViewController(/admin/**).setViewName(forward:/index.html); registry.addViewController(/user/**).setViewName(forward:/index.html); } }注意addViewControllers必须放在addResourceHandlers之后否则静态资源优先级高于fallback。4.2 现象MySQL插入中文乱码数据库里显示????原因MySQL服务端字符集未设为utf8mb4或JDBC连接URL缺少useUnicodetruecharacterEncodingUTF-8serverTimezoneAsia/Shanghai参数。解决MySQL服务端执行ALTER DATABASE community_repair CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE repair_order CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;检查application.yml中spring.datasource.url是否包含上述三个参数缺一不可。4.3 现象Vue打包后图片路径错误显示为/static/img/xxx.png但实际在/images/xxx.png原因Vue CLI默认将图片放入static目录但本方案要求图片存入static/images而vue.config.js未配置assetsDir。解决在vue.config.js中添加module.exports { assetsDir: images, // 所有静态资源img/css/js输出到static/images目录 // ... 其他配置 }4.4 现象登录后无法获取用户信息axios.get(/api/user/profile)返回401原因SpringBoot Security未放行/api/user/profile接口或JWT Token未正确传递。解决在Security配置中放行该接口Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers(/api/user/profile).authenticated() // 改为authenticated而非permitAll .antMatchers(/api/**).permitAll() // 其他API暂不鉴权毕设简化 .anyRequest().authenticated(); }Vue中确保每次请求携带Token// utils/request.js service.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers[Authorization] Bearer token; // 关键Header名必须是Authorization } return config; });4.5 现象答辩演示时点击“派单”按钮无反应浏览器Console报net::ERR_CONNECTION_REFUSED原因Vue开发服务器localhost:8080与SpringBoot后端localhost:8080端口冲突导致代理失效。解决修改Vue开发端口在vue.config.js中添加devServer: { port: 8081, // 改为8081 // ... 代理配置保持不变 }启动时明确指定端口npm run serve -- --port 8081确保SpringBoot仍运行在8080端口代理才能生效。5. 答辩实战技巧用3个可演示动作让老师记住你的项目价值答辩不是代码朗诵会而是用可感知的动作证明你理解了技术如何解决真实问题。以下三个动作经过12场校内答辩验证平均提升评分1.2分满分10分。5.1 动作一现场演示“状态流转可视化”用Druid监控页证明系统健壮性操作步骤启动SpringBoot项目打开浏览器访问http://localhost:8080/druid/login.html输入账号admin/ 密码admin123进入Druid监控页在左侧菜单点击SQL监控→ 右上角选择时间范围最近1分钟此时让同学用手机访问你的Vue前端提交一个报修单、物业派单、师傅上传图片回到Druid页面实时展示三条SQL执行记录INSERT repair_order、UPDATE repair_order SET statusassigned、INSERT repair_image为什么有效老师能看到真实数据库操作而非“理论上应该发生”Druid的执行耗时柱状图如repair_order插入耗时12ms直观证明性能达标点击任意SQL可查看完整执行语句和参数体现你对MyBatis底层的理解。我的学生曾因此被问“如果某次派单SQL执行超时你怎么定位”——答案直接指向Druid的慢SQL日志比背诵SpringBoot Actuator指标更有说服力。5.2 动作二用Chrome DevTools Network面板现场抓包分析一次完整业务链路操作步骤打开Vue前端F12进入DevTools → Network标签页勾选Preserve log防止页面跳转清空记录点击“提交报修单”按钮在Network列表中找到/api/repair/orders请求点击查看详情切换到Response标签展示返回的JSON{code:200,data:{id:105}}切换到Headers标签指出Content-Type: application/json;charsetUTF-8证明编码正确切换到Preview标签展开返回对象强调id字段是后端生成的自增主键关键话术“老师您看这个id:105不是前端生成的而是后端插入数据库后返回的真实主键。这意味着整个流程——从前端表单提交到后端校验、数据库写入、主键回填、JSON序列化返回——全部贯通。如果中间任何一环出错这里就不会出现id字段。”5.3 动作三对比“修改前vs修改后”用Git Diff证明你解决了真实痛点操作步骤打开IDEA右键点击RepairOrderController.java→Git→Show History找到最初提交Commit Message为“init controller”和最新提交Commit Message为“fix: status update race condition”右键最新提交 →Compare with Revision→ 选择初始提交在Diff窗口中聚焦三处修改Controller层新增RequestHeader(X-User-ID)参数校验Service层Transactional注解添加Mapper层updateStatusById方法的WHERE条件为什么打动老师展示你主动发现并修复了并发问题初始版本用先查后更新新版本改用乐观锁Git历史证明你持续迭代而非一次性堆砌代码Diff高亮让你能指着代码说“这里我加了事务因为派单必须和状态更新原子执行这里我改了SQL条件因为两个管理员同时派单会导致重复派单。”最后收尾时我习惯对学生说“答辩不是考你记住了多少API而是看你能不能让一个真实的人——比如你家楼下物业王师傅——用你的系统少跑一趟腿。当你能把‘居民报修’这件事从微信发消息变成系统里一条可追溯、可统计、可优化的数据流你就已经超越了90%的毕设。”希望帮到你。本文还有配套的精品资源点击获取
返回列表