
简介一套完整的微信小程序上门维修系统毕业设计源码面向高校计算机专业学生或开发者包含用户、维修员、管理员三类角色覆盖维修信息发布、记录跟踪、评价收藏以及后台综合管理等业务模块。项目基于Java、小程序和MySQL构建提供完整前后端源码与数据库文件可直接导入开发工具运行也适合作为毕业设计或课程设计的参考。压缩包内共1249个文件大小约26MB主要类型包括Vue页面、Java后端、小程序JavaScript、WXML/WXSS样式及数据库SQL脚本等目录结构清晰便于检索。已有87人学习下载按环境说明配置JDK1.8、Tomcat7、Maven3.3及微信开发者工具即可启动。除了可直接运行的系统还能学习多角色权限设计、前后端接口交互与移动端页面适配方法对快速搭建同类管理类小程序项目具有参考价值。1. 微信小程序毕业设计选上门维修系统一个能跑通完整闭环的务实题目拿到“上门维修系统”这个毕设题目你真正要做的是把一条业务线从用户端打通到师傅端用户在微信里发维修单师傅在小程序里接单、上门、完工管理员在后台处理审核和异常状态。这个题目比纯展示类的商城或公告栏更适合练手因为它的核心是一张维修订单围绕这张单子天然长出用户、师傅、服务分类、地址、评价几张关联表业务不算复杂但闭环完整。技术栈则非常贴近招聘市场常见组合Java 做后端接口MySQL 存业务数据微信小程序做两端界面再加上一篇 LW毕设圈子里通常指论文取“论文”二字拼音首字母作为文档材料正好凑齐毕业设计要的“系统 数据库 论文 演示”四件套。我见过不少同学解压这种源码包之后第一步就是照着 README 敲启动命令结果卡在环境变量、依赖版本和端口冲突上一下午没跑起来。这篇笔记按“包结构—数据库—后端接口—小程序联动—踩坑—演示验证”的顺序拆目标是让你拿到这个题目后能把它变成一套能在答辩现场流畅演示的系统同时在被老师追问“为什么这样设计”时能说出每个模块的真实理由。2. 先看懂包里有什么源码结构、LW 文档和运行前置条件2.1 LW 是论文而不是某个框架zip 交付物的通用组成很多第一次接触毕设源码包的同学会把“LW”当成一个技术名词去搜结果什么都搜不到。实际上在毕业设计交易和资源分享场景里LW 就是“论文”的拼音首字母缩写它不是一个运行组件而是一份 Word 文档通常包括开题报告、任务书、中期检查、论文正文和答辩 PPT。拿到压缩包后第一件事不是急着启动项目而是先理清目录否则很容易把论文目录里的截图误当成运行说明。一个规范的毕设源码包通常包含四块后端工程一个 Maven 项目、小程序前端工程一个微信开发者工具项目、SQL 脚本建库建表语句、LW 文档目录。有些还会附带 README 或部署说明。先做一次完整解压把目录树列出来确认后端、小程序、数据库脚本三样都在再开始配置环境。如果压缩包里有子目录被解压套了两层别忘了把里层目录提到外层否则后面导入 IDE 时路径全错。提示拿到包后先解压再核对目录。后端的 application.yml、小程序的 utils/request.js、SQL 脚本是三个最先要看的关键文件它们决定了整个系统怎么连起来。2.2 后端工程结构一个典型的 Spring Boot 分层上门维修系统的后端在毕设级别几乎都是 Spring Boot 单体工程很少拆微服务。常见结构是 controller、service、mapper、entity、config 五个包用 MyBatis Plus 或原生 MyBatis 操作 MySQL。拿到手先看 pom.xml确认依赖里有没有 MyBatis Plus、MySQL 驱动、Lombok、JWT 相关库这些决定了你接下来改代码的方式。dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies这段 pom 是毕设后端最常见的依赖组合。spring-boot-starter-web 提供 REST 接口能力MyBatis Plus 省去大量手写 SQL 的重复劳动Lombok 让实体类不用写 getter/setter。这三个依赖几乎是这类系统的基本盘如果包里的项目用的是别的持久层框架比如 Spring Data JPA也不奇怪但 MyBatis Plus 在中文毕设项目里出现频率更高因为它的代码生成器能快速生成 mapper 层。src/main/java/com/repair ├── controller # 接口入口接收小程序请求 ├── service # 业务逻辑订单状态流转都在这里 ├── mapper # MyBatis Plus 数据访问层 ├── entity # 数据库实体类 ├── config # 拦截器、跨域、MyBatis 配置 ├── common # 统一返回结果、异常处理、工具类 src/main/resources ├── application.yml # 数据源、端口、微信小程序配置 └── mapper # XML 文件写复杂 SQL 时使用这个分层的好处是边界清楚。controller 只做参数接收和结果返回service 里写订单状态判断、支付回调处理、并发接单这类业务逻辑mapper 只管数据库操作。答辩时老师问“某个功能在哪一层实现”你能直接指到对应文件比把所有逻辑堆在 controller 里要加分不少。接下来打开 application.yml确认数据库名、端口、小程序 appid 和 secret 是否和你本地环境一致。2.3 小程序端目录页面、公共方法和服务封装小程序端目录结构比后端直观。app.js 负责全局生命周期app.json 配置页面路由和窗口样式utils/request.js 封装 wx.requestpages 下面按功能分页面目录。上门维修系统的小程序通常有用户端和师傅端两个身份入口页面大概包括首页服务分类和推荐师傅、下单页、订单列表、订单详情、我的页面、师傅任务页。miniprogram/ ├── app.js ├── app.json ├── utils/ │ ├── request.js # 统一封装 wx.request │ └── util.js # 时间格式化等工具 ├── pages/ │ ├── index/ # 首页服务分类和 banner │ ├── order/ # 下单页填写地址与故障描述 │ ├── orderList/ # 订单列表区分用户/师傅视角 │ ├── orderDetail/ # 订单详情状态操作按钮 │ ├── profile/ # 我的个人信息与消息 │ └── worker/ # 师傅端任务列表与接单看小程序工程时先看 app.json 里的 pages 字段它决定哪些页面会出现在正式包里。再看 utils/request.js确认后端接口地址是写死的 IP 还是相对路径这直接影响你本机调试能不能通。多数毕设项目会在这里定义一个 BASE_URL比如 http://localhost:8080你本机跑微信开发者工具时要把 localhost 改成自己电脑的局域网 IP否则真机预览时手机会连不到电脑上的后端服务。2.4 环境清单与启动顺序先把依赖排干净不要一上来就双击 IDEA 导入先列一个环境对照表让所有软件版本对齐。JDK 用 1.8 最稳妥高版本 JDK 遇到旧项目可能出现反射和依赖兼容问题Maven 3.6 左右即可太新的 Maven 对旧仓库源也可能有兼容问题。MySQL 用 5.7 或 8.0 都可以但要注意 8.0 的认证插件和连接串参数与 5.7 不一样后面会专门讲这个坑。软件推荐版本用途注意点JDK1.8运行 Spring Boot高版本可能导致 Lombok 或旧依赖失效Maven3.6.3管理后端依赖用阿里云镜像加速依赖下载MySQL5.7 或 8.0存储订单与用户数据8.0 需配置 allowPublicKeyRetrieval微信开发者工具最新稳定版导入小程序工程调试需开启“不校验合法域名”IDEA2023 或更新导入 Maven 后端配置 Lombok 插件启动顺序也有讲究。先建数据库并执行 SQL 脚本再改 application.yml 里的数据库密码然后启动后端最后打开微信开发者工具导入小程序。颠倒顺序最常见的后果是后端起来了但报找不到表或者小程序先打开后因为后端没起而白屏。执行 SQL 时建议用 Navicat 或命令行 source 命令整段导入不要只复制其中一部分否则缺表或缺外键后面接口一调就报错。3. 数据库建模维修工单状态机与三张核心表3.1 工单状态机一张表看懂订单走到哪一步上门维修系统的业务核心不是用户表也不是师傅表而是维修订单。订单表里最重要的字段就是状态 status它决定了用户端显示什么按钮、师傅端能做什么操作、管理员在后台能看到哪些待处理数据。常见状态设计是 0 到 6 的数字枚举而不是直接存中文这样做既省空间又方便在代码里做条件判断。status含义用户端按钮师傅端操作0待接单等待师傅接单可取消看到抢单按钮1已接单待上门显示师傅联系方式联系用户标记已出发2维修中等待维修完成提交维修内容和费用3待支付支付维修费用等待用户支付4已完成查看订单详情查看订单详情5已取消无操作无操作状态机的设计直接决定 service 层代码怎么写。凡是涉及状态变更的接口比如接单、开工、完工、支付都要在 SQL 条件里带上“当前状态必须是某个值”否则会出现师傅一边维修用户一边取消订单的并发冲突。状态设计成数字而不是字符串还有一个好处是 switch 判断比字符串匹配更快而状态流转的合法性校验也更容易写。3.2 用户、师傅、订单三张表怎么建用户和师傅是两类不同角色。常见的做法是拆成两张表users 放微信用户基础信息worker 放师傅的审核状态、技能分类和服务区域。这样设计的好处是师傅的信息字段如审核状态、评分、接单数不会污染用户表用户和师傅的关联通过 user_id 完成。订单表则关联用户、师傅、服务分类和地址快照。CREATE TABLE user ( id bigint NOT NULL AUTO_INCREMENT, openid varchar(64) NOT NULL COMMENT 微信 openid, nickname varchar(64) DEFAULT , avatar varchar(255) DEFAULT , phone varchar(20) DEFAULT , user_type tinyint DEFAULT 0 COMMENT 0用户 1师傅, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_openid (openid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;这张用户表做了两个关键设计openid 加了唯一索引保证同一个微信用户只能有一条记录user_type 字段区分角色但不在表结构上做硬隔离因为一个用户既可以是下单者也可以申请成为师傅。nickname 和 avatar 是微信授权拿到的展示信息phone 则是在下单前由用户主动填写的联系号码这个字段在实际业务里通常不做强校验给正则校验就行。CREATE TABLE repair_order ( id bigint NOT NULL AUTO_INCREMENT, order_no varchar(32) NOT NULL COMMENT 业务订单号, user_id bigint NOT NULL COMMENT 下单用户, worker_id bigint DEFAULT NULL COMMENT 接单师傅, category_id bigint DEFAULT NULL COMMENT 服务分类, description text COMMENT 故障描述, address varchar(255) NOT NULL COMMENT 上门地址, latitude decimal(10,7) DEFAULT NULL, longitude decimal(10,7) DEFAULT NULL, appointment_time datetime DEFAULT NULL COMMENT 预约时间, status tinyint DEFAULT 0 COMMENT 状态0待接单, amount decimal(10,2) DEFAULT NULL COMMENT 维修费用, accept_time datetime DEFAULT NULL, finish_time datetime DEFAULT NULL, remark varchar(255) DEFAULT , create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_user_id (user_id), KEY idx_worker_id (worker_id), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT维修订单表;订单表把地址直接冗余在表里而不是通过地址 ID 去关联一张地址表这是上门服务类业务的特点订单里的地址是下单那一刻的快照之后用户改住址不影响历史订单。经纬度用 decimal(10,7) 存用于按距离推荐师傅。order_no 与自增主键 id 分开业务上展示订单号逻辑上用 id 做关联这样既避免在 URL 里暴露真实数据量也为将来分库分表留下空间。3.3 按距离找师傅经纬度查询是毕设里的加分项上门维修区别于普通电商的一个点是用户希望尽快找到附近的人。很多毕设只做到按分类筛选师傅如果能在下单页根据用户定位按距离排序推荐师傅这就是一个亮点。MySQL 里算距离有两种常见方式5.7 及以下用 Haversine 公式8.0 可以用内置的 ST_Distance_Sphere 函数。考虑到 SQL 脚本的兼容性用 Haversine 公式更稳妥。SELECT w.id, w.real_name, w.skill, w.score, (6371 * acos( cos(radians(#{lat})) * cos(radians(w.latitude)) * cos(radians(w.longitude) - radians(#{lng})) sin(radians(#{lat})) * sin(radians(w.latitude)) )) AS distance FROM worker w HAVING distance 5 ORDER BY distance ASC LIMIT 10;这条 SQL 把用户当前经纬度 #{lat} 和 #{lng} 传进去算出每位师傅与用户的球面距离过滤出半径 5 公里内的师傅并按距离排序。6371 是地球半径单位是公里弧度转换是公式的必要步骤数据库里存的经纬度是角度三角函数的参数是弧度不转算出来的距离会离谱。这个查询很适合放到 mapper XML 里配合 MyBatis 的 Param 注解传参。请注意表结构里如果师傅没有存经纬度就需要在 worker 表增加 latitude 和 longitude 两列。3.4 订单号生成别用自增 ID 当业务单号把自增 id 展示给用户看起来省事但有两个问题一是通过订单号能猜出平台单量数据敏感二是不同表各自有自增 id如果以后把订单表拆分单号会冲突。毕设里不用引入雪花算法那么重的方案但至少要用时间戳加随机数的组合生成可读的订单号。public String generateOrderNo() { SimpleDateFormat sdf new SimpleDateFormat(yyyyMMddHHmmss); String time sdf.format(new Date()); int random (int) ((Math.random() * 9 1) * 1000); return time random; }这段代码生成的是 18 位订单号14 位时间戳加 4 位随机数足够在单机毕业设计场景下避免重复。如果担心同一秒内并发两次下单撞随机数可以把随机数换成 AtomicInteger 自增或者利用 Redis INCR 生成每日自增序列。放在 service 层创建订单时调用不要在 controller 里生成这样业务逻辑内聚更好。4. 后端接口与小程序页面从下单到完工的完整请求链4.1 登录wx.login 换 openid再换 token小程序不像网页有用户名密码它依赖微信的 wx.login 拿到临时 code再把 code 发给后端由后端调用微信接口换取 openid。openid 是用户在某个小程序里的唯一身份标识同一个微信号在不同小程序里 openid 不同所以它非常适合做主键关联用户表。拿到 openid 后查用户表不存在就自动注册存在就直接登录。PostMapping(/login) public Result login(RequestBody WxLoginRequest req) { String url https://api.weixin.qq.com/sns/jscode2session ?appid appid secret secret js_code req.getCode() grant_typeauthorization_code; String result restTemplate.getForObject(url, String.class); JSONObject json JSONObject.parseObject(result); String openid json.getString(openid); User user userMapper.selectOne( new LambdaQueryWrapperUser().eq(User::getOpenid, openid)); if (user null) { user new User(); user.setOpenid(openid); user.setUserType(0); userMapper.insert(user); } String token JwtUtil.createToken(user.getId(), user.getUserType()); return Result.success(token); }这段登录接口做了三件事拿小程序传来的 code 去微信服务器换 openid查用户表决定登录还是注册最后签发 JWT token 给小程序端。后面所有需要身份的接口都在请求头里带这个 token由拦截器解析出用户 id 和角色。JWT 的好处是后端不用存 session适合小程序这种多端场景。代码里的 restTemplate 是 Spring 自带的 HTTP 客户端毕设里直接用它调微信接口就行不用再引其他依赖。小程序端对应的代码是 app.js 或者登录页里调用 wx.login拿到 code 后请求后端地址。wx.login({ success: (res) { if (res.code) { request(/user/login, POST, { code: res.code }) .then(data { wx.setStorageSync(token, data.token); wx.setStorageSync(userType, data.userType); wx.switchTab({ url: /pages/index/index }); }); } } });这段代码把后端返回的 token 和 userType 存到本地缓存后续每个请求从缓存里取 token 放在 header 中。userType 也需要存一份因为用户端和师傅端看到的页面入口完全不同。如果登录后跳转不正确优先检查后端返回里有没有 userType 字段很多毕设项目只返回 token 不返回角色导致小程序端无法区分身份。4.2 用户下单一条 POST 把订单写进去下单页收集的信息通常包括服务分类、故障描述、上门地址、联系方式和预约时间。后端下单接口要做的事是补全订单号、初始状态、下单用户 id然后插入订单表。这里有一个容易忽略的点地址信息应该直接保存用户填写的快照而不是在详情页通过地址 id 再查一次。PostMapping(/order/create) public Result createOrder(RequestBody RepairOrder order) { if (order.getAddress() null || order.getAddress().trim().isEmpty()) { return Result.error(上门地址不能为空); } order.setOrderNo(generateOrderNo()); order.setStatus(0); order.setUserId(currentUserId()); orderMapper.insert(order); return Result.success(order.getOrderNo()); }下单接口的校验不能只靠前端前端可以绕过所以后端至少做两个校验地址非空、服务分类合法。status 强制设为 0 而不是信任前端传值是为了防止别人通过抓包伪造一个已接单的订单。订单号在 service 里生成controller 层只负责参数接收。这里没有做金额校验因为维修费用通常由师傅维修完成后报价而不是下单时确定这也符合上门维修的真实流程。4.3 师傅接单用 UPDATE 加状态条件防并发抢单抢单是上门维修系统里最有技术含量的点之一。同一个订单如果两个师傅同时点击接单后端的查询接口在瞬间都查到 status0如果都直接执行 UPDATE 设置 status1就会出现两个师傅同时接同一单。解决办法不是加锁而是在 UPDATE 语句里带上状态条件让数据库自己保证只有一个更新成功。UPDATE repair_order SET status 1, worker_id #{workerId}, accept_time NOW() WHERE id #{orderId} AND status 0这行 SQL 是防并发接单的关键只有当前订单状态还是 0 时接单更新才会生效。MyBatis 的 update 方法返回影响行数如果返回 1 说明抢单成功返回 0 说明订单已经被别人接走直接返回“手慢了订单已被接”。这种写法叫作乐观锁不用数据库行锁性能好而且逻辑简单。很多同学在这个地方用 select 先查再 update中间没有事务隔离并发下必翻车。4.4 订单列表小程序页面列表加载更多的标准配合订单列表是用户端和师傅端最常见的页面。小程序的列表交互有两种下拉刷新和触底加载更多。下拉刷新用 onPullDownRefresh触底加载用 onReachBottom。后端接口必须支持分页参数否则本地开发时数据少看不出问题数据一多页面就会卡。GetMapping(/order/list) public Result list(RequestParam Integer page, RequestParam Integer size, RequestParam(required false) Integer status) { PageHelper.startPage(page, size); ListRepairOrder orders orderMapper.selectByStatus(status); return Result.success(orders); }PageHelper 是 MyBatis 的分页插件调用 startPage 之后接下来第一条查询会自动拼接 LIMIT。这里的 page 是页码从 1 开始size 是每页条数。接口返回里应该有 total 字段让前端判断是否还有下一页。小程序端的 onReachBottom 则负责在页面触底时累加页码并重新请求。onReachBottom() { const list this.data.list; if (list.length this.data.total) { return; } const page this.data.page 1; this.setData({ page: page, loading: true }); this.loadOrders(); }这段代码先判断当前已加载条数是否已经等于总数如果相等就停止加载避免做无用请求。loadOrders 里按 this.data.page 请求列表拿到数据后用 concat 追加到已有数组而不是整体覆盖。页面底部通常会显示“加载中”或“已经到底了”这个小细节在演示时很加分因为老师很可能直接上手滑动列表。4.5 进度反馈与完工状态机走到哪一步接口就开放哪些动作第 3 章的状态机设计在这里真正发挥作用。当订单处于“维修中”时师傅端提交完工接口后端先校验当前状态是 2再更新为确认费用状态同时写入完工时间。如果用户此时想取消订单后端校验发现 status 已经是 2就拒绝取消并提示“维修已开始请联系师傅协商”。状态校验统一放到 service 层做一个独立方法避免每个接口重复写 if 判断。public boolean checkStatus(Long orderId, int expectStatus) { RepairOrder order orderMapper.selectById(orderId); return order ! null order.getStatus() expectStatus; }调用方写起来就是接单前 checkStatus(orderId, 0)开工前 checkStatus(1)完工前 checkStatus(2)。这个简单封装让状态流转逻辑集中在一起老师问起状态机设计时你可以直接把这张状态迁移表拿出来讲。如果源码包里的逻辑不是这样组织的建议你按这个思路重构一遍代码量不大但对答辩很有帮助。5. 跑通系统时最常见的 5 个坑环境、端口、字段和中文乱码排查5.1 MySQL 8 连接报 Public Key Retrieval is not allowed现象后端启动时控制台报错提示 Public Key Retrieval is not allowed数据库无法连接。这个错误只出现在 MySQL 8.0原因是默认认证插件 caching_sha2_password 在非 SSL 连接下需要先获取公钥而 JDBC 驱动默认不允许自动获取公钥。原因项目里 JDBC URL 缺少 allowPublicKeyRetrieval 参数或者用的是旧的 mysql-connector-java 5.x 驱动连接 MySQL 8。解决方式是两条路要么在连接串里加上 allowPublicKeyRetrievaltrue要么把 MySQL 用户的认证方式改回 mysql_native_password。毕设里改连接串最快。spring: datasource: url: jdbc:mysql://localhost:3306/repair?useSSLfalseallowPublicKeyRetrievaltruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456这段连接串里同时处理了三个隐患useSSLfalse 避免本地开发时的 SSL 警告allowPublicKeyRetrievaltrue 解决 8.0 认证问题characterEncodingutf8 保证中文写入serverTimezoneAsia/Shanghai 避免时区错误导致时间差 8 小时。如果是 MySQL 5.7最后两个参数仍然有用第一个参数不影响。5.2 真机调试请求失败域名校验和局域网 IP 的坑现象在微信开发者工具里接口调通预览到手机后所有请求全部 fail页面白屏。这是小程序开发最经典的一个坑原因是开发者工具有一个默认设置“不校验合法域名”工具本地调试时自动绕过域名校验但真机上微信小程序运行时强制要求所有请求域名是 HTTPS 且在后台配置过。原因后端跑在 http://localhost:8080这个地址手机根本访问不了而且 http 接口在真机上默认被拦截。解决方式分两层开发调试阶段在后端启动的电脑上查局域网 IPipconfig 或 ifconfig把小程序请求的 BASE_URL 改成 http://192.168.x.x:8080在微信开发者工具里勾选“不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书”。注意这个勾选只是开发阶段的后门演示时如果必须用真机确保手机和电脑连同一个 WiFi并且电脑防火墙放行 8080 端口。5.3 中文写库变成问号连接串和表字符集现象下单成功后数据库里故障描述和地址全部是问号英文数字正常。这种问题几乎都出在字符集不一致而不是代码逻辑。常见情况是 MySQL 表默认字符集是 latin1或者 JDBC 连接串没指定 utf8。原因MySQL 连接时客户端、连接层、表结构三层字符集不一致。解决方式是一层层对齐建表语句里显式 CHARSETutf8mb4连接串加 characterEncodingutf8还可以在 MySQL 命令行执行 SET NAMES utf8mb4 让当前会话生效。注意 utf8mb4 和 utf8 的区别utf8mb4 是 MySQL 8 和 5.7 都推荐的完整 UTF-8 实现支持 emoji 和生僻字utf8 在 MySQL 里是 utf8mb3 的别名遇到特殊字符可能报错。检查时用 SHOW CREATE TABLE repair_order直接看表定义里的 DEFAULT CHARSET 是什么。5.4 后端端口被占用与 SQL 脚本执行一半现象后端启动时报端口占用或者执行 SQL 脚本时报错中断导致部分表存在部分表缺失。端口问题非常好排查启动日志里写着 Port 8080 was already in use说明有别的进程占着 8080。原因电脑上其他 Java 项目或服务占用端口也可能是上次启动的后端没停干净。解决方式一是在 application.yml 里把 server.port 改成 8081同时记得改小程序端的 BASE_URL方式二是找到占用进程然后终止Windows 下用 netstat -ano | findstr 8080 查 PID再用 taskkill /PID 进程号 /F。SQL 脚本执行一半通常是脚本里有重复语句或者某个表的字符集兼容问题Navicat 会停在出错的语句上把那条语句单独拿出来分析。如果是 utf8mb4_0900_ai_ci 报错说明脚本是 MySQL 8 生成的要在 5.7 里把排序规则改成 utf8mb4_general_ci。5.5 登录接口通了却分不清用户和师傅现象小程序登录成功跳转页面却不对用户端能看到师傅的操作按钮或者师傅端看不到待接单列表。原因大多是后端签发的 token 里没有包含角色信息前端拿到 token 但不知道当前登录者是用户还是师傅。解决方式是在登录接口返回里显式带上 userType前端存储后判断跳转更严格的做法是后端写一个拦截器解析 token 后根据 userType 拦截师端接口。毕设项目里常见的是前者只做前端路由跳转判断虽然安全性一般但演示效果足够。如果你想加分可以在后端加一个注解 RequireRole(1) 来标记师傅端接口拦截器里校验 userType这是比较完整的权限设计方案。6. 演示前必做的验证动作让答辩演示不翻车的检查清单6.1 最小演示路径一张单从下单到评价跑一遍演示不要从头开始讲代码而是按业务顺序走一条最短路径用户登录小程序发布一个维修订单切到师傅端登录抢单更新为维修中提交完工费用再切回用户端支付并评价。这条路径走完系统最重要的功能点全部展示到了。演示前至少完整跑三遍第一遍按正常节奏第二遍故意快速操作看状态是否错乱第三遍清掉数据后重新初始化确保不是依赖上一次的残留数据才能跑通。6.2 预置数据与快照给演示留后悔药现场演示最怕的是数据库被误删或状态改乱。提前用 mysqldump 导出一份包含预置账号、服务分类、几条待接单订单的快照脚本比现场手动重来可靠得多。mysqldump -u root -p repair demo_backup.sql这段命令导出的备份里包含全部表结构加已插入的数据。提前把这份 SQL 放进项目目录万一演示中途状态改乱直接执行 source demo_backup.sql 就能恢复到初始状态。我的习惯是准备两个管理员账号和两个测试用户并记录在小纸条上避免现场临时翻手机找密码。6.3 现场兜底接口报错时如何快速恢复到可用状态答辩现场网络环境不可控最稳妥的演示方式是提前打开好后端、数据库和开发者工具不要在评委面前重新启动整个链路。如果真机联调失败立刻切到开发者工具演示工具里不受真机网络限制。如果接口报错但页面还在就用浏览器或 Postman 直接打接口把返回 JSON 展示给老师看至少证明后端逻辑没问题。开发阶段我吃过一次亏演示前一晚为了调样式把小程序 appid 换成自己测试号第二天登录全部失效从那以后我在演示前一天绝对不动环境配置只改数据内容。这些细节看着不起眼却决定了你半年的代码在十分钟里是被认可还是被质疑。希望这篇笔记能帮你把这个毕设题目变成真正属于自己的作品。本文还有配套的精品资源点击获取