Spring Boot+小程序+MySQL实战:上门维修系统毕设源码这样学才有价值
简介面向计算机专业毕业设计/课程设计场景的微信小程序上门维修系统源码采用Java小程序MySQL架构涵盖用户、维修员、管理员三个角色。用户端可查看首页、广告、新闻资讯并管理维修信息、维修记录、评价与收藏维修员端可处理维修信息、维护记录与评价管理员后台则提供用户管理、维修员管理、维修信息管理、评价管理、广告管理和系统管理等完整功能。压缩包共1249个文件约26.02MB以Java源码、Vue页面、JS脚本、WXML/WXSS小程序页面、PNG图片和SQL数据库文件为主前后端代码与数据库脚本齐全目录清晰便于定位与二次开发。目前已有87人学习适合需要快速搭建同类课题或参考完整项目结构的同学使用。1. 上门维修系统毕业设计源码包java小程序mysqlLW 组合到底在解决什么问题每年毕设季标题「上门维修系统源码java小程序mysqlLW.zip」的压缩包在班群里被转来转去下载的人多真正能跑起来并讲明白的人连一半都不到。不是代码难而是很多人拿到压缩包之后不知道先改哪里、后启动什么也有人拿了源码直接交老师一问到底层就卡壳。这套源码对应的是一条「用户小程序下单 Java 后端派单 MySQL 存订单」的完整业务闭环LW 目录一般就是答辩要用的论文或文档。它适合两类人软件工程、计算机相关专业的应届生需要一套能现场演示、能二次开发的毕业设计以及刚学过 java 基础和 mysql 入门想找个微信小程序项目实例练手的初学者。我的建议是别把它只当交差工具而是当成一个带需求、带页面、带状态机的全栈训练场后面你会省下大量返工时间。2. 先盘清架构再动手三层结构怎么分工本地从零跑通需要几步2.1 为什么是 Spring Boot 原生小程序 MySQL而不是 uniapp 或前端分离先回答一个高频疑问标题里写了「小程序」为什么不用 uniapp 一套代码跑三端因为这个项目大概率是原生的微信小程序工程目录里应该是 app.json、pages、components 这种结构而不是 uniapp 的项目配置。原生和 uniapp 在毕业设计里最大的差别是原生小程序加载更快微信开发者工具里的报错更直接uniapp 的优势在多端复用但你还得额外维护一套编译链对毕设来说性价比不高。如果手里源码用的是原生就干脆死磕原生改起来反而省心。后端选 java 几乎是这类课程的默认选择不是它最优雅而是资料最多、面试最好解释。常见组合是 Spring Boot MyBatis-Plus MySQL。MyBatis-Plus 的好处是单表增删改查基本不用手写 SQL可以把注意力放在订单状态机这样的核心逻辑上。「java 面试题」里常考的依赖注入、事务、数据一致性在这个项目里都能找到对应落点。别的不说光看 mysql 安装配置教程这个关键词每年有多少人搜就知道环境问题才是第一道坎。2.2 本地跑通最小环境清单JDK、Maven、MySQL、开发者工具我一般先把环境列成一张表缺哪个补哪个不要一上来就解压源码。组件推荐版本用途JDK8 或 11运行 Spring Boot 后端Maven3.6拉依赖、打包IDEA / Eclipse近两年版本即可打开 java 工程MySQL5.7 或 8.0建库导入 SQLNavicat for MySQL任意可视化导入、查表微信开发者工具稳定版运行小程序端关于版本有个血泪经验MySQL 8.0 默认认证方式和 5.7 不一样连接串少写两个参数就会报 SSL 相关错误JDK 太新比如 17和某些老的 Spring Boot 2 组合也会出现反射警告但一般能跑。如果导师没有硬性要求我建议用 JDK 8 MySQL 8.0这是当前资料最齐、踩坑记录也最全的组合。2.3 导入源码的四步操作建库、改配置、起后端、开小程序把压缩包解开常见结构是一个后端目录放 java 工程一个小程序目录放前端外加 LW 文档目录。第一次启动别急着翻源码按下面四步走。第一步建库并导入 SQL。用 Navicat 新建一个数据库名字随意比如repair然后右键运行 SQL 文件。导入时注意字符集选utf8mb4不然之后插入中文会变成问号。命令行也可以mysql -uroot -p CREATE DATABASE IF NOT EXISTS repair DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin; USE repair; SOURCE /path/repair.sql;COLLATE utf8mb4_bin按需使用不关心大小写排序的话默认的utf8mb4_general_ci也够用。SOURCE后面要写 SQL 文件的实际绝对路径导入完成后在 Navicat 里刷新看到 t_user、t_order 这类表就算成功。提示Maven 首次拉依赖可能很慢在settings.xml里配阿里云镜像能省下大量等待时间。第二步改后端数据源配置。用 IDEA 打开后端目录等 Maven 把依赖拉完先不急着点启动打开src/main/resources/application.ymlserver: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/repair?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver这里重点说三个参数useSSLfalse是本地开发开关MySQL 8 默认开 SSL不加它可能出现自签名证书报错serverTimezoneAsia/Shanghai解决日期相差 8 小时allowPublicKeyRetrievaltrue为了配合caching_sha2_password认证插件不加容易报Public Key Retrieval is not allowed。端口 8080 被占就改成 8081但后面小程序端 BASE_URL 也要同步改。第三步启动后端。在 IDEA 里直接点运行类看到启动日志里有 Tomcat started on port 8080 就算起来。也可以用命令行mvn spring-boot:run第四步打开微信开发者工具导入小程序目录。AppID 选「测试号」就行开发阶段不需要注册。关键是在「详情-本地设置」里勾选「不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书」否则后面 wx.request 会直接报域名校验失败。四步走完正常情况下能看到小程序首页打开后端日志出现请求记录。如果卡在某一环先别急着怀疑源码按第 5 章的排查表一条条对。很多「跑不起来」其实都是环境没配对。3. Java 后端核心模块维修订单状态机、接口设计与数据一致性3.1 t_user 与 t_order 的表结构设计字段默认值怎么设才不挨答辩骂答辩老师最爱问的一句就是「你这个表为什么这样设计」。源码包里的表结构不一定最优但你要在跑通之后能说出理由。以用户表为例CREATE TABLE t_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 用户ID, openid VARCHAR(64) NOT NULL COMMENT 微信openid唯一标识, nickname VARCHAR(32) DEFAULT COMMENT 昵称, avatar VARCHAR(255) DEFAULT COMMENT 头像URL, role TINYINT NOT NULL DEFAULT 1 COMMENT 1-客户 2-维修工 3-管理员, phone VARCHAR(20) DEFAULT COMMENT 手机号, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, UNIQUE KEY uk_openid (openid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;openid 是用户在微信生态里的唯一标识所以必填且加唯一索引role 用 TINYINT 存数字而不是字符串为了查询快、占空间小这是回答「为什么不用枚举」的常见说法。create_time 交给数据库默认值保证后端代码漏填时也不至于落空值。订单表是核心字段多状态尤其关键CREATE TABLE t_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL COMMENT 订单号展示用, user_id BIGINT NOT NULL COMMENT 下单用户ID, worker_id BIGINT DEFAULT NULL COMMENT 接单维修工ID, category VARCHAR(32) NOT NULL COMMENT 故障类型水管/电路/家电等, address VARCHAR(255) NOT NULL COMMENT 上门地址, description TEXT COMMENT 故障描述, status TINYINT NOT NULL DEFAULT 1 COMMENT 1待接单 2已接单 3维修中 4待支付 5已完成 6已取消 7退款中, appoint_time DATETIME DEFAULT NULL COMMENT 预约上门时间, price DECIMAL(10,2) DEFAULT NULL COMMENT 维修报价, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_status (status), KEY idx_user (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT维修订单表;注意status默认值设置成 1对应「待接单」。这是毕设项目里非常典型的默认值也对应一道经典 mysql 面试题状态字段用数字类型存储默认值必须和状态枚举的初始态对齐否则新订单落库后查不到。另一个高频追问是worker_id为什么允许 NULL因为订单创建时还没人接单这个值本来就是空的等接单接口再回填。3.2 登录接口小程序 wx.login 的 code 换 openid再签发 token小程序端没有 cookie常见做法是用wx.login拿到一次性 code发给 Java 后端后端再拿 code 向微信服务器换 openid。注意方向不是小程序直接拿 openid而是后端去换这样 appid 和 secret 才不会暴露在小程序代码里。接口大概长这样PostMapping(/api/user/login) public Result login(RequestBody LoginRequest request) { // 1. 用 code 调微信 jscode2session 接口拿到 openid 和 session_key MapString, Object session wxService.jscode2session(request.getCode()); // 2. 根据 openid 查用户表查不到就说明是第一次使用自动注册 User user userMapper.selectOne(new LambdaQueryWrapperUser() .eq(User::getOpenid, session.get(openid))); if (user null) { user new User(); user.setOpenid((String) session.get(openid)); userMapper.insert(user); // 默认 role 1即普通客户 } // 3. 签发本地 token后续接口都靠它识别身份 String token JwtUtil.createToken(user.getId(), user.getRole()); return Result.ok(token); }三个细节值得记。第一jscode2session必须放在后端因为微信接口要求 appidsecret 一起传泄露到前端等于裸奔。第二selectOne查不到再 insert 在高并发下可能重复注册但因为 user 表有openid唯一索引第二次 insert 会抛DuplicateKeyExceptioncatch 后再查一次即可这是回答「java 怎么保证数据一致性」的一个实证。第三token 里只放 userId 和 role过期时间一般设 7 天。3.3 下单与派单接口状态机约束非法流转防止同单双接上门维修的订单生命周期是「待接单 → 已接单 → 维修中 → 待支付 → 已完成」中间还可能被客户取消。最怕前后端各自为政前端隐藏按钮、后端不校验结果出现「已取消的订单还能被接单」这种答辩现场翻车。所以后端必须做状态机校验。接单接口核心逻辑如下PostMapping(/api/order/accept) public Result accept(RequestBody AcceptDTO dto, RequestHeader(token) String token) { Long workerId JwtUtil.parseToken(token).getUserId(); Order order orderMapper.selectById(dto.getOrderId()); if (order null) { return Result.error(404, 订单不存在); } // 状态机约束只有 status1待接单时才允许转成 2已接单 if (order.getStatus() ! 1) { return Result.error(403, 订单当前状态不可接单); } // 用 update 条件写死 status1防止两个维修工同时接同一单 int rows orderMapper.updateStatus( dto.getOrderId(), workerId, 1, 2); if (rows 0) { return Result.error(403, 手慢了订单已被其他维修工接走); } return Result.ok(); }这段代码的精髓是用一条 update 解决「并发双接」SQL 里WHERE id? AND status1只有恰好读到 status1 的请求能成功另一个 update 影响行数为 0直接提示失败。这比先 select 再 update 稳得多也是我反复强调的「状态机放后端而不是前端」。实际源码可能没这么严谨但你答辩时能背着这一段讲出来比照本宣科强很多。另外如果订单有支付环节支付回调接口必须设计成幂等同一订单的支付结果可能被微信服务端推送多次后端要先按order_no查状态只有待支付才更新为已完成否则会把已完成订单覆盖成别的状态。多数毕设只接模拟支付但这个思路一定要有。4. 小程序端从登录到下单wx.request 联调、顶部导航栏与缓存时间4.1 小程序页面与 tabBar 配置符合微信小程序项目实例的常见写法小程序端打开后先看app.json它是整个小程序的地基。常见配置长这样{ pages: [ pages/index/index, pages/order/create, pages/order/list, pages/order/detail, pages/mine/mine ], window: { navigationBarTitleText: 上门维修, navigationBarBackgroundColor: #2f54eb, navigationBarTextStyle: white }, tabBar: { list: [ { pagePath: pages/index/index, text: 首页 }, { pagePath: pages/order/list, text: 工单 }, { pagePath: pages/mine/mine, text: 我的 } ] } }提示tabBar 页面路径不能带参数需要跳转的详情页要作为普通页面用 wx.navigateTo 打开。tabBar里的路径必须在pages数组里存在tabBar 页面不能带参数跳转。如果你想从首页跳到一个带orderId的详情页详情页不能放进 tabBar只能用wx.navigateTo打开。pages 第一项就是启动页一般放首页。关于「微信小程序顶部导航栏高度」自定义导航栏时最容易翻车。默认 iOS 导航栏 44pxAndroid 是 48px但胶囊按钮位置在不同机型上不一样。沿用默认 window 配置就不用管高度如果源码用了自定义导航组件一定要用wx.getMenuButtonBoundingClientRect()动态计算胶囊位置再定 header 高度。写死 44px 在安卓上会明显偏上这种玄学 bug 改了半天其实就一行代码的事。4.2 登录态与缓存时间带时间戳的 wx.setStorage 实现小程序登录态不能只存一个 token因为 token 过期后用户还在页面上一请求就失败。常见做法是把 token 和过期时间一起存请求前先判断const BASE_URL http://localhost:8080 function authRequest(path, method, data) { const cache wx.getStorageSync(loginCache) // 如果缓存存在但已过期先清理本地登录态 if (cache cache.expireAt cache.expireAt Date.now()) { wx.removeStorageSync(loginCache) wx.navigateTo({ url: /pages/mine/mine }) return } wx.request({ url: BASE_URL path, method: method || POST, data: data || {}, header: { token: cache ? cache.token : }, success: (res) { if (res.statusCode 401) { // token 失效重新走 wx.login login() } } }) }把过期时间一起存就是「微信小程序设置缓存时间」的经典解法不依赖 wx.setStorage 的默认永久生效而是自己控制有效期。每次读取先看expireAt过了就清理避免用户卡在半登录状态。实际源码里可能只是简单wx.setStorageSync(token, ...)但你可以改成这种结构答辩时多一个可讲的点。登录入口一般放在onLaunch或自定义login()函数里function login() { wx.login({ success: (res) { wx.request({ url: BASE_URL /api/user/login, method: POST, data: { code: res.code }, success: (r) { wx.setStorageSync(loginCache, { token: r.data.data.token, expireAt: Date.now() 7 * 24 * 3600 * 1000 }) } }) } }) }注意wx.login的 code 是一次性的后端换完 openid 后立刻失效所以不要把这个 code 存下来重复用这是第 5 章会提到的坑。调试时小程序端 Network 面板能看到每个 wx.request 的完整请求和响应后端控制台也有日志两头对一下时间戳基本能定位大部分联调问题。4.3 故障上报与图片上传wx.chooseMedia 联调 Java 接口上门维修系统里用户最少要传一段故障描述好一点的产品还会让用户拍照。小程序端上传图片现在推荐wx.chooseMedia而不是老接口wx.chooseImagewx.chooseMedia({ count: 3, mediaType: [image], sizeType: [compressed], success: (res) { const tasks res.tempFiles.map((item) { return new Promise((resolve) { wx.uploadFile({ url: BASE_URL /api/file/upload, filePath: item.tempFilePath, name: file, success: (r) resolve(JSON.parse(r.data)) }) }) }) Promise.all(tasks).then((files) { // 拿到后端返回的文件url后和表单一起提交 this.setData({ imageUrls: files.map(f f.data.url) }) }) } })count: 3限制一次最多选 3 张sizeType: [compressed]让微信压缩后再上传省流量也省后端存储。Promise.all等三张图都传完再提交表单避免图片地址还没写完就点提交。后端对应的PostMapping(/api/file/upload)接口一般接收MultipartFile保存到本地目录后返回可访问的 URL。这里提醒一句生产环境小程序要求所有请求域名都是 HTTPS不然 iOS 上经常出现「已连接但无法验证服务器身份」的报错本地调试则靠「不校验合法域名」绕过。5. 避坑清单导入、联调、部署最常见的 5 个翻车现场这一章专门写踩坑记录每条都按「现象 → 原因 → 解决」展开遇到问题可以直接按图索骥。5.1 MySQL 连接翻车error 2002 socket 与 SSL 连接错误现象Maven 工程启动时控制台报error 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock或者连库时抛Connection refused又或者 Navicat 能连但后端连不上。原因第一种是 MySQL 服务根本没起来Mac 上通过 Homebrew 安装的 MySQL 默认 socket 在/tmp/mysql.sock服务未启动时这个文件不存在第二种是 MySQL 8.0 默认开启 SSL 和caching_sha2_password连接串缺少useSSLfalse和allowPublicKeyRetrievaltrue时后端会报 SSL 连接错误。解决先确认服务状态再改连接串。命令行执行mysql.server startMac或检查 Windows 服务里的 MySQL 是否运行。连接串统一加这段?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue如果还不行用 Navicat 连一次确认账号密码没问题再回来检查后端配置。这类问题八成是复制配置时少了某个参数不是源码 bug。5.2 小程序 request 域名校验本地能通、上线必挂现象微信开发者工具里wx.request报url not in domain list或者真机预览时请求一直失败局域网 IP 地址同样连不上。原因小程序不像浏览器可以随意跨域所有请求域名必须在小程序后台配置且必须是 HTTPS。本地开发时项目默认启用域名校验任何http://localhost或http://192.168.x.x都会被打回。注意真机预览时 BASE_URL 必须改成电脑的局域网 IP不能写成 localhost。解决开发阶段在「详情-本地设置」勾选「不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书」。真机预览需要电脑和手机连同一局域网BASE_URL 改成电脑局域网 IP。上线前必须买域名、配 HTTPS 证书并在小程序管理后台「开发管理-服务器域名」里添加 request 合法域名。这个坑每届毕设都会踩几乎成了过来人标配忠告。5.3 wx.login 的 code 被重复使用接口回 40029 invalid code现象小程序端第一次登录成功第二次进入时后端返回40029 invalid code或者页面里偶尔登录失败。原因wx.login返回的 code 只能使用一次后端调用jscode2session之后立即作废。很多同学把 code 存到wx.setStorageSync里下次进入页面直接拿旧 code 去换 token自然报无效。解决不要缓存 code每次需要登录或续期时重新调用wx.login()。如果发现同一页面里多次请求确保只调一次wx.logincode 用完即弃。后端也要排查是否在调试时手动重复消费了同一次 code。5.4 MyBatis-Plus 驼峰映射失效接口返回全 null现象后端接口调用成功但 JSON 里的createTime、userId这类驼峰字段全是 null数据库里明明有值。原因表字段是下划线风格create_time、user_idJava 实体用的是驼峰createTime、userIdMyBatis-Plus 的驼峰映射开关没生效或实体类上少了TableField注解。解决确认application.yml里有下面这一行它在 MyBatis-Plus 控制下划线转驼峰mybatis-plus: configuration: map-underscore-to-camel-case: true如果开了还不行去实体类字段上加TableField(create_time)强制映射。排查顺序很重要先看全局配置再看注解最后才怀疑数据库字段写错。5.5 端口被占用与多实例冲突后端静默起不来现象IDEA 启动后没有报错但访问接口迟迟连不上或者mvn spring-boot:run启动到一半卡住日志里有Port already in use。原因8080 端口被上一个残留进程占住Spring Boot 默认端口不可用就报异常退出。还有同学一台电脑开了多个后端实例导致数据库连接池耗尽表现为请求超时而不是拒绝连接。解决先用命令查端口占用lsof -i :8080 kill -9 PIDWindows 上用netstat -ano | findstr 8080。想换端口就把application.yml里的server.port改成 8081再同步改小程序端 BASE_URL。建议先在后端启动日志里确认实际监听的端口再决定要不要排查避免对着 8080 找半天的无效劳动。排查时可以按这个顺序后端起没起 → 数据库连没连 → 小程序本地设置 → 接口入参 → 日志。不要在第一步还没确认时就盯着小程序端代码反复看那是毕业设计季最常见的无效劳动。6. 答辩前加一个「维修评价」闭环顺手做接口自检如果时间有余我建议在这个毕业设计上加一个「评价」功能订单完成后客户对维修工评分和留言。一个功能同时补齐业务闭环和数据库设计两个答辩加分点而且改动不大。先建评价表CREATE TABLE t_review ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_id BIGINT NOT NULL COMMENT 订单ID一个订单只能评一次, user_id BIGINT NOT NULL COMMENT 评分人, worker_id BIGINT NOT NULL COMMENT 被评维修工, rating TINYINT NOT NULL DEFAULT 5 COMMENT 1-5星, content VARCHAR(500) DEFAULT COMMENT 评价内容, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_order (order_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT维修评价表;uk_order唯一索引是业务约束一单只能评一次。后端新增接口时必须先在 service 里校验订单状态必须是「已完成」再校验操作人就是订单下单人最后用order_id执行 insert碰到DuplicateKeyException就提示已评价。这一步和 3.3 节是同一件事把约束写在数据库和业务层而不是只靠前端按钮。写完后做一轮自检我习惯用 curl 打三个路径登录、创建工单、评价。curl -X POST http://localhost:8080/api/user/login \ -H Content-Type: application/json \ -d {code:test_code} curl -X POST http://localhost:8080/api/review/add \ -H token: 上面返回的token \ -H Content-Type: application/json \ -d {orderId:3,rating:5,content:上门快修得好}如果登录接口依赖微信 code本地测试时可以临时在接口里放一个测试分支或者直接用数据库里现成订单的 userId 构造一个 token目标只是验证状态机和权限判断是否生效。再加一步慢 SQL 检查在 MySQL 里执行EXPLAIN SELECT * FROM t_order WHERE status 2能走到idx_status索引就可以顺手写进论文的性能分析小节。我自己的习惯是拿到任何毕设源码第一件事保留一份原始 SQL 备份改一张表就重新导出一遍。这个习惯救过我很多次至少比在答辩前花一整晚恢复数据库心态要稳。这个方向投入不多看源码、改状态机、加评价功能几晚就能做完但它会让你的项目从「能运行」变成「能讲清楚、经得起追问」希望帮到你。本文还有配套的精品资源点击获取