ARTICLE DETAIL

资讯详情

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

Spring Boot+微信小程序:上门维修服务系统设计与实现

Spring Boot+微信小程序:上门维修服务系统设计与实现 1. 毕设选题拆解上门维修服务系统到底要做什么先说结论这个题目本质上是做一个“师傅端用户端”的撮合平台只不过把载体从传统的Web网页换成了微信小程序。用户打开小程序就能提交维修需求、预约时间、在线支付、查看师傅进度师傅通过小程序接单、上门、完工结算管理员在后台管理用户、师傅、订单、工单和结算数据。核心关键词就是springboot和微信小程序前者承担后端接口和业务逻辑后者承担用户触达和移动端交互。选这个题做毕设有个天然优势场景非常生活化人人都懂。家电坏了、水管漏了、灯泡不亮了用户需要的不是自己去修而是“快速找到靠谱的师傅并且知道师傅来没来、修没修完”。所以系统最核心的能力是“下单—派单—接单—服务—验收—结算”这条完整链路。基于微信小程序而不是独立App又可以免去安装成本用户扫一扫就能用这正好贴合维修服务这种低频、即时、本地化的场景。从毕设评分角度这类题目评委普遍关注三件事一是业务逻辑是否完整有没有把“预约、派单、工单、评价”这些关键节点做透二是技术栈是否主流springboot加微信小程序属于非常成熟且常用的组合答辩时能讲清楚原理三是项目是否真实可运行手里握着源码和演示截图比什么都强。如果能把订单状态机、微信登录、支付回调、消息通知这些细节做出来这个题目拿高分并不难。2. 技术选型与整体架构为什么是springboot加微信小程序2.1 springboot在后端的角色定位springboot现在几乎是Java后端开发的默认起点。它的核心价值是“约定大于配置”不需要像传统SSH那样写一堆XML依赖spring-boot-starter-web就能快速起一个Web服务。对于毕设这种规模的项目springboot自带的内嵌Tomcat、自动配置、健康检查等能力完全够用部署时直接打成jar包扔到服务器上就能跑对不熟悉运维的学生来说非常友好。在这个系统里springboot主要负责三块事情接口服务给微信小程序提供RESTful API包括用户登录、维修下单、订单查询、师傅接单、完工上传、支付回调等。业务处理维修单的状态流转、技师匹配、价格计算、评价统计等核心逻辑都在后端完成前端只做展示和交互保证数据一致性和安全性。数据持久化通过spring-data-jpa或mybatis操作MySQL把用户、订单、师傅、工单等数据落地到数据库。我当时用的是MyBatis-Plus理由很简单单表CRUD几乎不用写SQL内置的分页插件也方便做订单列表的加载更多。配合springboot的自动装配整个数据访问层的代码量能压缩一半以上。2.2 微信小程序作为客户端的优势选择微信小程序不是跟风而是它确实适合这个场景。维修服务是典型的“低频刚需”用户家里一年可能也就修两三次东西让用户为了这种事专门下载一个App留存率会非常低。小程序用完即走、微信内直接打开完美匹配“有需要时才用”的节奏。小程序端要做的页面包括首页展示服务分类、推荐师傅、优惠信息报修/下单页选服务类型、描述故障、上传图片、选择地址和预约时间订单列表和详情查看状态、联系师傅、支付、评价个人中心管理地址、查看工单、设置这里要特别说一点小程序的界面设计不要贪多核心是“下单流程要顺畅”。一个用户打开小程序最好三步以内就能提交报修单选分类、填地址、预约时间。页面上需要的信息越少越好后端能自动推导的字段比如用户ID、下单时间就不要让用户填。2.3 整体架构分层设计项目结构我建议分为四层虽然很多人觉得毕设不用讲究分层但答辩时这是加分项Controller层接收请求做参数校验返回统一响应体Service层处理业务逻辑比如订单状态校验、派单规则、支付回调处理Mapper/Repository层数据库读写Domain/Entity层实体类和数据传输对象另外微信小程序端和后端之间通过HTTPJSON通信登录态用token维护。小程序每次请求在header里带上token后端通过拦截器验证身份这样既安全又简单比session更适配无状态服务。3. 功能模块设计与数据库建模3.1 核心角色与权限划分这个系统涉及三类角色普通用户、维修师傅、系统管理员。不同的角色看到的界面和能调用的接口完全不同所以后端在接口设计时要区分好用户类型比如角色核心功能小程序端/后台用户下单、支付、评价、查看订单小程序端师傅接单、查看工单、上传完工照片、结算小程序端师傅版管理员审核师傅、分配或改派订单、查看统计、管理服务分类管理后台Web很多同学会忽略师傅端独立入口的问题。实际开发时可以在同一个小程序里通过角色标识显示不同界面也可以通过不同的小程序AppID区分用户端和师傅端。毕设建议用前者省事且演示方便登录后根据角色路由到对应首页即可。3.2 核心数据表设计数据库是整个系统的基础表设计合理后期写代码能省很多事。我列出必建的核心表user表用户和师傅共用一张表用role字段区分避免两套用户体系带来的麻烦。字段包括openid、昵称、头像、手机号、角色、状态等。service_category表服务分类比如家电维修、管道疏通、开锁换锁。字段包括分类名称、图标、排序、是否启用。repair_order表维修订单主表。字段包括订单编号、用户ID、分类ID、故障描述、图片、地址、预约时间、期望价格、状态、师傅ID、实际费用、创建时间等。work_order表工单表师傅接单后生成记录上门时间、完成时间、施工描述、物料费用、完工照片。evaluation表评价表关联订单和师傅字段包括评分、内容、标签等。payment_record表支付记录表保存微信支付回调的订单号、金额、状态。message_notice表通知消息表用于下单、派单、完工等节点的消息推送。这里有个容易忽略的细节订单状态建议用数字枚举比如0待支付、1待派单、2待服务、3服务中、4待验收、5已完成、6已取消、7退款中。状态流转要画清楚不然写接口时容易逻辑混乱。3.3 订单状态机设计状态机是这套系统最核心的业务逻辑。我踩过的坑是一开始把状态更新散写在各个Service方法里结果改来改去把自己绕晕了。后来我才用一个OrderStateMachine类集中管理每次状态变更都要走这个方法校验当前状态是否允许目标状态并记录状态变更历史。正常流程是这样用户提交报修单生成待支付订单支付成功变成待派单管理员或系统按距离和评分派给师傅变成待服务师傅上门开始维修变成服务中师傅提交完工信息变成待验收用户确认无误后点完成变成已完成之后可以评价评价后整个闭环结束。要特别注意“取消”分支。用户支付前可以随时取消支付后如果师傅还没接单可以申请退款师傅接单后原则上不能随意取消需要联系客服处理毕设里可以在后台加一个管理员强制取消的接口。4. SpringBoot后端核心实现细节4.1 项目搭建与依赖引入用IDEA创建springboot项目Java版本建议8或11springboot版本不要追求最新选2.7.x这种相对稳定的版本就行。太高版本可能和某些依赖有兼容问题而且答辩时老师也不关心你用多新的版本。核心依赖就这么几个dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.x/version /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId /dependencyapplication.yml里配置数据源和MyBatis-Plus示例spring: datasource: url: jdbc:mysql://localhost:3306/repair_system?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: mapper-locations: classpath:mapper/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl注意IDEA里创建项目时如果联网拉取依赖失败检查Maven镜像源是否配置为阿里云仓库。很多新手卡在第一步就是因为默认的中央仓库在国内访问太慢。4.2 微信小程序登录与token鉴权小程序登录是第一个要做的功能。流程是小程序端调用wx.login拿到code把code发给后端后端拿着code加上appid和secret去微信接口换openid然后用openid查本地用户表如果用户不存在就自动注册最后生成一个token返回给前端。后端核心代码如下PostMapping(/login) public Result login(RequestBody LoginRequest request) { String url https://api.weixin.qq.com/sns/jscode2session?appid appid secret secret js_code request.getCode() grant_typeauthorization_code; String response restTemplate.getForObject(url, String.class); JSONObject json JSONObject.parseObject(response); String openid json.getString(openid); // 查询或创建用户 User user userMapper.selectOne(...); // 生成token String token UUID.randomUUID().toString().replace(-,); redisTemplate.opsForValue().set(token, user.getId().toString(), 7, TimeUnit.DAYS); return Result.success(token); }用Redis存token是为了能设置过期时间并且支持服务端主动清理。如果纯内存Map也行但重启就失效了维护体验差。毕设用Redis也不难本地装一个即可。如果不想依赖Redis也可以采用JWT方式把过期时间编码在token里后端不需要存状态实现更简单。4.3 维修单创建与订单号生成用户提交报修单时后端要做的是校验参数、生成唯一订单号、计算订单初始状态、写入数据库。订单号我采用“日期随机数”的方式形如202501011530123456为了防止并发重复加一个随机四位数字。MySQL的索引能保证唯一即使碰撞插入时也会报错捕获后重新生成即可。接单功能的核心是锁。师傅点击接单时必须保证同一订单只能被一个师傅抢到。最简单的做法是使用数据库条件更新int update repairOrderMapper.update(null, new LambdaUpdateWrapperRepairOrder() .eq(RepairOrder::getId, orderId) .eq(RepairOrder::getStatus, 1) // 待派单状态 .set(RepairOrder::getStatus, 2) // 待服务 .set(RepairOrder::getMasterId, currentUserId)); if (update 0) { return Result.error(手慢了订单已被接走); }这个做法本质上是乐观锁用状态作为版本条件更新影响行数为0说明状态已经被别人改了。这种写法简单可靠比先查询再更新安全得多。4.4 微信支付接入与回调处理很多同学对支付环节很恐惧其实毕设里不需要真的做完整支付链路。两种方案一是接入微信支付沙箱/真实商户号但需要企业资质学生一般申请不了二是做“模拟支付”在测试环境里点击支付按钮直接跳转支付成功后台记录一条支付流水并标记支付状态为已支付即可。如果采取模拟方案要在答辩时明确说明“这是模拟支付真实项目中接入微信支付原生流程只需替换支付接口”。同时模拟支付也要保留支付流水表方便展示业务完整性。如果你确实申请到了支付接口后端需要处理的就是支付回调PostMapping(/pay/notify) public String payNotify(RequestBody String xmlData) { // 解析微信返回的XML校验签名 // 更新payment_record表和repair_order表状态 return xmlreturn_code![CDATA[SUCCESS]]/return_code/xml; }注意回调接口必须返回微信规定的XML字符串否则微信会不断重试回调导致重复更新。5. 微信小程序端设计与实现要点5.1 小程序项目结构与请求封装微信小程序的目录结构相对固定。建议把根目录下的utils、components、pages、api分清楚。尤其要单独建一个request.js封装统一的网络请求包括基础域名、token注入、错误处理。这里贴一个简化版请求封装const request (url, method, data) { return new Promise((resolve, reject) { wx.request({ url: baseUrl url, method: method, data: data, header: { Content-Type: application/json, token: wx.getStorageSync(token) }, success: (res) { if (res.data.code 200) { resolve(res.data.data); } else { wx.showToast({ title: res.data.msg, icon: none }); reject(res.data); } }, fail: reject }); }); };封装之后页面里调用接口就是一行request(/api/order/list, GET, params)代码干净很多。同时不要忘了在开发工具里勾选“不校验合法域名”否则本地调试时请求会失败。上线前再把域名配置到小程序后台的request合法域名里。5.2 首页与分类服务列表首页不要堆太多东西。推荐布局是顶部搜索框、中部服务分类宫格、下方推荐师傅列表。服务分类数据从后端接口获取点击某个分类后进入下单页并自动选中该分类。这里推荐一个技巧服务分类的图标建议用云存储或对象存储的链接不要塞到项目本地包里。因为一个小程序包最大只有2MB图片多就超了。当时我把minio纳入图片存储就是出于这个考虑——维修师傅上传的完工照片、用户报修上传的故障图都需要一个存储服务。毕设里也可以用七牛云或腾讯云COS有免费额度。如果你不想用云服务最简单的方案是让用户用相机拍照后把图片转成base64传后端再由后端拼接保存到本地静态目录只是占数据库空间大一点测试场景凑合能用。5.3 订单列表与加载更多“加载更多”是实现列表常见的需求。微信小程序的onReachBottom触发下拉到底时增加请求页码。后端用MyBatis-Plus分页小程序端维护一个pageNum变量每次加载下一页时都接着上次的offset。注意返回数据中最好带hasMore字段避免一直发请求。核心逻辑let pageNum 1; const pageSize 10; loadOrders() { request(/api/order/list, GET, { pageNum, pageSize }).then(res { this.setData({ orderList: this.data.orderList.concat(res.records), hasMore: res.records.length pageSize }); }); }, onReachBottom() { if (this.data.hasMore) { pageNum; this.loadOrders(); } }这里有个小坑分页接口返回的records为空数组就说明没有更多数据了不要拿它做唯一判断依据。因为最后一条数据刚好填满一页时还会有一次空请求。5.4 师傅端界面与完工流程师傅端的关键页面是“待接单大厅”和“我的工单”。在待接单大厅里师傅可以看到附近待派单的维修需求列表点击接单按钮后调用后端接单接口。接单后工单状态变成“待服务”师傅需要点击“开始上门”再点击“完工提交”上传现场照片和填写材料费用。完工提交是最有演示效果的功能。建议让师傅在页面上传三张照片故障部件照片、维修过程照片、完工后照片再填写服务描述。后端把这些信息存到work_order表。用户端看到这些信息才能信任师傅所以这个功能不仅是为了毕设效果也体现了业务的真实感。6. 常见问题与排查技巧实录6.1 微信开发者工具无法加载数据表现是request请求一直报fail或者url not in domain list。排查顺序首先看开发工具右上角是否开启了“不校验合法域名”没开就勾选一下其次看后端的端口是否被本地防火墙拦截确认localhost:8080能否用浏览器直接访问最后检查请求封装里baseUrl是否写错了协议或IP注意微信开发者工具请求本地后端时不能用localhost最好用局域网IPhttp://192.168.x.x:8080同时手机调试时也要保持手机和电脑在同一个Wi-Fi。6.2 springboot启动时报端口被占用这是最常见的启动报错。解决分三步第一步netstat -ano | findstr 8080查到占用端口的PIDWindows下用taskkill /F /PID 进程号杀掉第二步如果还是不行在你的后端配置文件里换一个不常用端口比如8090同时改小程序请求地址第三步检查是否开了多个后端实例。一个小的习惯把IDE里的“Allow parallel run”关掉避免重复启动。6.3 数据库中文乱码乱码几乎100%是连接字符集问题。在JDBC连接URL后加上characterEncodingutf8同时确认MySQL表结构的字符集是utf8mb4而不是默认的latin1。有同学改完JDBC还不行那是因为建表的时候字符集已经是错的要重建表或执行ALTER TABLE xxx CONVERT TO CHARACTER SET utf8mb4。6.4 小程序登录后token失效如果token用Redis存储检查Redis的键是否设置了过期时间。我用的时候把过期时间设成了7天但用户在7天内一直操作是没问题的。如果用户隔几天再来打开小程序token失效前端需要在响应拦截器里判断code为401时自动调用登录接口重新拉取token并更新本地存储。不要指望用户手动重启小程序。6.5 订单状态更新丢失问题前面提到的乐观锁是最保险的但还有另一个隐患前端连续发送多个请求导致状态跳变。比如用户点“确认完成”时手抖点了两下后端接单接口被并发调用两次。解决办法在后端接口入口处加入分布式锁可以用Redis的setnx或直接依赖数据库的乐观锁更新。毕设里用乐观锁加唯一请求ID基本就够。7. 一些毕设避坑与答辩建议最后说点实在的。第一不要把毕设做成“功能堆砌”一个维修服务系统最重要的功能就是下单、派单、接单、完工、评价这五个闭环节点。宁可把闭环做流畅也不要东加一个秒杀、西加一个聊天功能多了出错面也大答辩时老师问你“这个功能和主线的关系是什么”很容易被问懵。第二保证演示环境稳定。答辩当天最怕的就是网络差、Redis启动失败、数据库连接不上。我建议提前准备好一套本地演示环境所有服务都在本机跑同时录制一份操作视频作为备份。当时我就是因为现场Wi-Fi连不上连手机热点也慢最后直接播视频才过关那叫一个惊险。第三把“为什么”想清楚。springboot为什么不用SSH小程序为什么不用H5订单为什么用状态机而不是布尔值支付为什么要回调而不是前端直接改状态这些基础问题老师几乎必问。你不需要把底层源码全部讲透但至少要能用自己的话说出选型的理由。第四代码注释和命名规范一定要做好。答辩时有老师会直接打开你的代码看如果你的Service层有那种几百行的“上帝方法”即便功能没问题也会被扣印象分。哪怕不重构也要把关键业务逻辑拆成几个小方法并写上注释。这个题目真正有价值的地方在于它让你完整走了一遍真实应用的开发过程移动端、服务端、数据库、状态管理、权限控制、文件存储。做完你会发现后面无论换成外卖、跑腿还是预约系统核心骨架几乎都一样。把这个系统吃透比那种为了凑数堆了十多个模块的“大杂烩”毕业设计有用得多。我自己的体会是把一条主线做到极致本身就是最好的答辩准备。
返回列表