简介:一个面向校园互助场景的微信小程序毕业设计项目,携带完整源码与数据库脚本,适合计算机相关专业的学生用于毕业设计、课程大作业或项目实战练习。项目通过导师指导并获评审98分,内容经助教审定,难度适中;源码均经过本地编译与严格调试,可放心运行。压缩包为zip格式,共2000个文件,约48.77MB;其中包括1198个js文件处理小程序页面交互,352个css文件负责界面样式,还有java、jsp等后端代码实现服务逻辑,sql数据库脚本用于初始化数据,md文档则提供说明参考,目录结构清晰便于检索。资源内置基于Bootstrap与AdminLTE的管理页面,可快速配置互助信息发布与审核流程;配套的数据库脚本能一键搭建初始环境,减少了环境配置时间。已有141人学习下载,对于需要参考完整高分开题方案与可运行代码的读者很有价值。
1. 微信小程序校园互助平台:为什么这个“源码+数据库”型毕设值得做
每年毕业设计季,总有人在“管理系统”和“电商小程序”两个模板里打转,前者只剩增删改查,后者又牵涉支付权限,工作量容易失控。基于微信小程序的校园互助平台是容易被低估的一个选项:要处理用户登录、需求发布、接单、完成、评价这条完整业务链路,还得回答“多人同时接同一单”这类实际问题,数据模型和接口都有话可讲,演示时也能现场操作。标题里的“源码+数据库”意味着交付物有可运行工程和初始化 SQL,这类材料不少见,但真正能一次跑通的并不多,多数卡在数据库编码、登录凭证、图片路径这三个环节。这篇笔记按“数据表→小程序端→后端接口→避坑→答辩验证”的顺序把复现过程讲透,适合准备拿它做毕设或课程设计的同学,也适合想快速熟悉小程序前后端联调的人。
2. 从互助场景倒推数据库:先定表和状态机,再写任何一行接口
拿到一套“源码+数据库”项目,我习惯先打开 SQL 文件而不是先跑代码。数据库设计是这个项目的骨架,表怎么拆、字段怎么定,直接决定后面被导师提问时能不能把逻辑讲圆。把场景拆开:一个学生登录后发布“求带饭”“帮取快递”这类需求,另一个学生看到后接单,完成后双方评价,中途还能取消。围绕这个闭环,只需要四类实体:用户、需求单、分类、评价记录。类目和图片这类附属信息能合并就合并,不要一上来就拆十几张表,那会给后期维护添麻烦。
2.1 用户、需求、评价与类目:从四个真实场景推导出的数据表
先给一个底层的装入方式。表建议四张起,最多五张,超过就想想是不是过度设计:
| 表名 | 作用 | 关键字段说明 |
|---|---|---|
| user | 小程序用户 | openid 唯一,学号用于实名,信用分可做奖惩 |
| help_order | 互助需求单 | 发布人、接单人、状态、类型、期望回报、图片 |
| comment | 订单评价 | 所属订单、评价人、被评价人、评分、内容 |
| category | 需求分类 | 可选;如果只用 TINYINT 存类型常量,可以不要这张表 |
我自己会保留 category 表,虽然网上很多项目直接写死类型枚举,但答辩时“为什么把分类做成表”比“为什么类型用数字”更好回答。分类表能支撑以后加“帮带早餐”这类新类目,属于一个低成本的功能扩展点,分摊到开发成本上几乎可以忽略。
2.2 建库建表 SQL:字段类型、索引与逻辑外键的一次性决定
下面这一段 SQL 按 MySQL 5.7/8.0 都能直接执行的标准来写,utf8mb4 字符集必须在一开始就定好,否则后患无穷。
CREATE DATABASE IF NOT EXISTS campus_help DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE campus_help; CREATE TABLE user ( id INT AUTO_INCREMENT PRIMARY KEY, openid VARCHAR(64) NOT NULL COMMENT '微信登录唯一标识', student_no VARCHAR(32) DEFAULT NULL COMMENT '绑定学号', nickname VARCHAR(64) DEFAULT NULL, avatar VARCHAR(255) DEFAULT NULL, phone VARCHAR(20) DEFAULT NULL, credit INT DEFAULT 100 COMMENT '信用分', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_openid (openid) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; CREATE TABLE category ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(32) NOT NULL, sort TINYINT DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='需求分类'; CREATE TABLE help_order ( id INT AUTO_INCREMENT PRIMARY KEY, order_no VARCHAR(32) NOT NULL COMMENT '对外订单号', title VARCHAR(100) NOT NULL, content TEXT, category_id INT DEFAULT 0, type TINYINT DEFAULT 0 COMMENT '0快递 1代购 2失物 3拼车 4跑腿', reward DECIMAL(10,2) DEFAULT 0.00 COMMENT '酬劳金币', status TINYINT DEFAULT 0 COMMENT '0待接 1已接 2完成 3取消', publisher_id INT NOT NULL, acceptor_id INT DEFAULT NULL COMMENT '接单用户ID', image_urls VARCHAR(1024) DEFAULT NULL COMMENT '逗号分隔的图片访问地址', location VARCHAR(128) DEFAULT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, accept_time DATETIME DEFAULT NULL, finish_time DATETIME DEFAULT NULL, UNIQUE KEY uk_order_no (order_no), KEY idx_status_type (status, type), KEY idx_publisher (publisher_id), KEY idx_acceptor (acceptor_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='互助需求单'; CREATE TABLE comment ( id INT AUTO_INCREMENT PRIMARY KEY, order_id INT NOT NULL, from_user_id INT NOT NULL, to_user_id INT NOT NULL, rating TINYINT DEFAULT 5 COMMENT '1到5星', content VARCHAR(255) DEFAULT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_order (order_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单评价';字段里值得反复确认的是 order_no:用独立订单号,而不是直接把自增 id 暴露给前端。一是避免“拿到一个 id 就能遍历全部数据”的隐患,二是订单号可以带上日期前缀,日后对账和演示都直观。status 和 type 用 TINYINT 而不是 VARCHAR,编码层面省空间,索引也更快,前端展示时再做字典翻译。
关于外键:这个建表语句里没有一条物理 FOREIGN KEY,全部用逻辑外键关联。原因很现实,小程序场景高并发抢单时,物理外键会让每次更新多一次约束检查,毕设阶段还会增加数据导入顺序的麻烦。导师如果追问,就回答“逻辑外键在应用层保障一致性,满足当前业务粒度,也方便后续分表”。
2.3 状态机:需求单从发布到完成经历的四个状态
给需求单设计状态,是这个项目里少数能直接体现出业务思考的地方。我把状态定为 4 个:0 待接单,1 已接单,2 已完成,3 已取消。必须明确每个状态能往哪里走,不能允许从已完成再退回已接单。
0 待接单可以转向 1 已接单或 3 已取消;1 已接单只能由发布者确认完成,也可以由发布者取消;2 已完成是终态,之后只允许产生评价记录;3 已取消也是终态。接单人不能随意把单子丢回去,取消动作统一由发布者发起,这在校园场景里相当于撮合方说了算,规则简单,答辩时不会被绕晕。
代码里可以用一个枚举类把状态和文案钉死,避免页面里散落魔法数字:
public enum OrderStatus { WAITING(0, "待接单"), ACCEPTED(1, "已接单"), FINISHED(2, "已完成"), CANCELLED(3, "已取消"); private final int value; private final String text; OrderStatus(int value, String text) { this.value = value; this.text = text; } }后续所有状态变更接口,都只允许传入目标状态,并在 Service 层校验“当前状态是否允许转移”。这一步校验不复杂,但能让你的代码在流程严谨度上和第二档的模板项目拉开距离,也是答辩时一个稳定的加分提问点。
3. 小程序端实现:登录、请求封装、发布和接单的完整链路
数据库立住之后,小程序端是用户直接看到的部分。我一般先写请求封装,再做页面,因为所有页面都要跟后端打交道,封装不统一,后面每写一个页面都会重复贴 wx.request,代码会迅速膨胀。登录之外,还要把 token 存进本地缓存,并在后端返回 401 时统一踢回登录页,而不是每个接口单独判断。
3.1 登录链路:wx.login 换 openid,再绑定学号
微信小程序没有传统意义上的账号密码表单,登录的第一步是调用 wx.login 拿到一个临时 code,把这个 code 交给后端,后端再拿 code 向微信服务端换取 openid。openid 是每个用户在当前小程序下的唯一标识,它应该作为 user 表的唯一键,而不是让用户自己填手机号注册。
// utils/auth.js const login = async () => { const res = await new Promise((resolve, reject) => { wx.login({ success: resolve, fail: reject }); }); if (!res.code) { wx.showToast({ title: '获取登录凭证失败', icon: 'none' }); return null; } const data = await request('/api/auth/login', 'POST', { code: res.code }); wx.setStorageSync('token', data.token); wx.setStorageSync('userInfo', data.userInfo); return data.userInfo; };逻辑说明:wx.login 成功后返回的 code 有效期很短且只能用一次,所以不要把它存起来过一阵子再用。后端拿着 code 去微信的jscode2session接口换 openid 和 session_key,换完立即失效。前端拿到 token 后直接存 storage,后续请求都在 header 里带过去。
绑定学号是提高项目完整度的一步:登录后如果userInfo.studentNo为空,弹一个表单让用户填学号,调用/api/user/bindStudentNo。这个字段在“校园”场景中几乎是必问的,也是后面“我的发布”和“我的接单”互相隔离的天然依据。
3.2 请求封装:统一处理 token、错误码和 loading
开发时最容易翻车的地方不是业务逻辑,而是每个页面对 wx.request 的成功失败处理都不一样,一旦后端改了返回格式,前端要改的地方会让人炸毛。我会把所有请求收敛到一个方法里,后端统一返回{ code: 200, data: ..., message: ... },遇到业务错误直接 toast,页面只关心成功分支。
// utils/request.js const BASE_URL = 'http://localhost:8080/api'; const request = (url, method = 'GET', data = {}) => { return new Promise((resolve, reject) => { wx.request({ url: BASE_URL + url, method, data, timeout: 10000, header: { 'token': wx.getStorageSync('token') || '' }, success(res) { const body = res.data; if (body.code === 200) { resolve(body.data); } else if (body.code === 401) { wx.removeStorageSync('token'); wx.reLaunch({ url: '/pages/login/login' }); reject(body); } else { wx.showToast({ title: body.message || '请求失败', icon: 'none' }); reject(body); } }, fail(err) { wx.showToast({ title: '网络异常,请稍后再试', icon: 'none' }); reject(err); } }); }); };参数提示:timeout 设成 10000 毫秒比较合适,校园网高峰期请求会在 5 秒左右超时,设太短页面容易误报;BASE_URL 本地联调时用http://localhost:8080/api,真机预览时再换局域网 IP。token 的过期时间不要放在前端算,后端返回 401 时统一跳登录页,比在前端“设置缓存时间”要可靠得多,因为你无法保证小程序端时间和服务器时间完全一致。
3.3 发布页、广场列表与接单操作:最占交互量的三块页面
发布页表单字段要与 help_order 表一一对应:标题、分类、地点、报酬、详情、图片。几个经验值:title 限制 30 字以内,reward 用 DECIMAL(10,2) 后端校验不超过 9999,图片最多 3 张,单张压缩后再传。图片选择推荐用 wx.chooseMedia,并且指定 sizeType 为 compressed,不然学生手机一拍就是 5MB,上传失败率和后端带宽都会被拖垮。
// pages/publish/publish.js wx.chooseMedia({ count: 3, mediaType: ['image'], sizeType: ['compressed'], success: async (res) => { const tasks = res.tempFiles.map(file => new Promise((resolve, reject) => { wx.uploadFile({ url: BASE_URL + '/api/file/upload', filePath: file.tempFilePath, name: 'file', header: { 'token': wx.getStorageSync('token') }, success: r => { const body = JSON.parse(r.data); if (body.code === 200) resolve(body.data.url); else reject(body); }, fail: reject }); })); const urls = await Promise.all(tasks); this.setData({ imageUrls: urls.join(',') }); } });广场列表是承接流量的核心页,很多同学只顾静态渲染,忘了分页。后端接口约定为GET /api/order/list?page=1&pageSize=10&type=0,前端用 onReachBottom 触底加载下一页,用 onShow 而不是 onLoad 刷新列表,因为从发布页回来或者从详情页返回时,列表里的状态可能已经变了。
// pages/square/square.js Page({ data: { list: [], page: 1, finished: false }, async refresh() { const data = await request('/api/order/list', 'GET', { page: 1, pageSize: 10, type: -1 }); this.setData({ list: data.records, page: 1, finished: data.records.length < 10 }); }, async loadMore() { if (this.data.finished) return; const next = this.data.page + 1; const data = await request('/api/order/list', 'GET', { page: next, pageSize: 10, type: -1 }); this.setData({ list: this.data.list.concat(data.records), page: next, finished: data.records.length < 10 }); }, onShow() { this.refresh(); } });接单按钮放在订单详情页里,点击后先弹 wx.showModal 确认,再调POST /api/order/accept。这里有个值得主动演示的交互细节:如果接口返回“该需求已被接走”,不要只弹一个 toast,要把这条记录从当前列表里真正删掉或置灰,用户才会感知到实时状态变化。这也是答辩时可以展示的亮点:开启两个账号,同一时间点接同一单,只有一个会成功。
4. 后端源码跑通:Spring Boot 配置、接口约定与并发接单
源码拿到手里,最耗时间的往往是环境而不是业务。我的复现顺序是:先建库导 SQL,再改数据库连接,最后启动 Spring Boot 主类。如果一开始就去翻接口代码,很容易被复杂的包结构绕进去。后端结构按 Controller、Service、Mapper 三层拆是最稳的,Controller 只做参数接收和出参封装,Service 里写状态机和事务,Mapper 用 SQL 直接处理原子更新。
4.1 让源代码在本地跑起来的配置文件与启动顺序
多数类似项目的连接配置都在 src/main/resources/application.yml 里,只需要改数据库地址、账号、密码。下面这段是必调参数,少一个都可能出现时区报错或中文乱码:
server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/campus_help?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver servlet: multipart: max-file-size: 5MB max-request-size: 20MB mybatis-plus: configuration: map-underscore-to-camel-case: true逻辑说明:characterEncoding=utf8负责保证写库中文不乱码,serverTimezone=Asia/Shanghai解决 MySQL 8 驱动默认 UTC 时区带来的 8 小时偏移,map-underscore-to-camel-case让数据库里的create_time自动映射到 Java 的createTime,省掉一堆 XML 手写字段。数据库连接池这里用的是 Spring Boot 默认的 HikariCP,不需要额外配置,毕设并发场景下默认连接数足够。
启动顺序上,我习惯先确认数据库里表已建好,再运行主类,看到控制台打印出 Tomcat started on port 8080 才算通。不要急着联调页面,先用 Postman 或 curl 打一下登录接口,能返回 token,源码才算真正跑起来了。
4.2 核心接口约定:登录、发布、接单、完成、取消与评价
前后端联调阶段,最容易出问题的是接口路径和参数名不一致。下面这组是我在校园互助平台里常用的约定,前端 request.js 的 BASE_URL 正是对这套路径:
| 功能 | 方法与路径 | 关键参数 | 说明 |
|---|---|---|---|
| 登录 | POST /api/auth/login | code | 返回 token 和用户信息 |
| 绑定学号 | POST /api/user/bindStudentNo | studentNo | 需要 token |
| 发布需求 | POST /api/order/save | title、content、type、reward、location、imageUrls | 需要 token |
| 广场列表 | GET /api/order/list | page、pageSize、type | type=-1 表示全部 |
| 接单 | POST /api/order/accept | orderId | 需要 token |
| 完成订单 | POST /api/order/finish | orderId | 只有发布者可调 |
| 取消订单 | POST /api/order/cancel | orderId | 发布者可在 0 或 1 状态取消 |
| 评价 | POST /api/order/comment | orderId、rating、content | 订单完成后可调 |
参数说明中有一个容易踩的点:reward 虽然叫酬劳,但校园互助通常不会真走微信支付,我用的是平台虚拟币或积分,数据库字段仍然是 DECIMAL(10,2),方便以后接入支付而不改动表结构。订单完成动作要校验登录用户是发布者,接单动作要校验状态是 0,这两条校验写在 Service 里而不写在 Controller,因为 Controller 层做太多事务逻辑会很难测试。
4.3 并发接单:为什么不能用“先查询再更新”,要用一条 UPDATE 兜底
毕设答辩现场最容易死于这个追问:“两个用户同时点接单,你都判断状态是 0,然后都执行了更新怎么办?”常规写法是先select status from help_order where id = ?,看到 0 再 update。两次操作之间有空窗,并发时两个请求都读到 0,就都更新成功,订单被两个人接走,数据就脏了。
正确的做法是让更新条件直接带上状态,做一个原子操作:
UPDATE help_order SET status = 1, acceptor_id = #{userId}, accept_time = NOW() WHERE id = #{orderId} AND status = 0如果这条 UPDATE 影响的行数是 1,说明接单成功;影响行数是 0,说明状态已经不是 0,需求已被抢走。Mapper 接口对应方法返回 int,Service 里判断返回值即可:
@Service public class OrderService { @Autowired private HelpOrderMapper orderMapper; @Transactional public boolean acceptOrder(Long orderId, Long userId) { int rows = orderMapper.acceptOrder(orderId, userId); return rows == 1; } }逻辑说明:@Transactional保证万一后续要插入接单记录、扣积分时,这些操作能一起成功或一起回滚。用“UPDATE ... WHERE id AND status=0”的方式,本质是数据库的行锁和条件更新解决了并发窗口,不需要引入 Redis 分布式锁,对毕设体量来说正好。这里也解释了为什么我在建表时把 status 的单列索引建上,更新条件里的 status 是高频过滤字段,索引能避免全表扫描。
5. 避坑:把“源码+数据库”复现成功前,最常见的 5 个翻车点
从拿到代码到成功演示,我总结过的经验是:代码本身出 bug 的概率,远小于环境配置、编码、域名证书这三类问题。下面这 5 条是我在这个项目上反复见到的现象,每一条都按“现象→原因→解决”来描述,照着排除能省下来大半天。
5.1 开发者工具里列表页全空,控制台提示“不在合法域名列表”
现象:在小程序开发者工具里打开项目,请求后端接口全部失败,报错文案是“url 不在以下合法请求域名列表中”。原因:微信安全规则要求请求域名必须是 HTTPS,而本地开发用的是http://localhost:8080,自然被拦截。解决:本地调试阶段打开开发者工具右上角“详情-本地设置”,勾选“不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书”,调试请求就能发出去了。这个开关只影响工具,不影响真机。真正要拿手机演示时,要么把后端部署到带 HTTPS 域名的服务器上,要么退回开发者工具模拟器演示。
5.2 导入 SQL 后中文变问号,页面上的需求标题全是乱码
现象:用可视化工具导入 sql 文件后,打开表数据,中文变成??或�。原因:多半是建库时没有指定 utf8mb4 字符集,导出的 SQL 文件本身也是 GBK,或者连接 URL 里缺少characterEncoding=utf8。解决:重新执行创建数据库语句,指定字符集再导入:
CREATE DATABASE campus_help DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE campus_help; SOURCE /path/to/campus_help.sql;导入时注意:sql 文件头部如果有SET NAMES utf8mb4;可以保留,不要删。后端连接 URL 里也必须带上characterEncoding=utf8,只改数据库还不够,连接池方向的编码同样会出事。
5.3 图片上传成功,但详情页的 image 一直 404
现象:上传接口返回了一个 URL,拉到页面上显示裂图。原因:后端把文件保存到了本地磁盘,但 Spring Boot 默认没有把这些目录映射成静态资源访问路径,浏览器拿到 URL 后自然找不到文件。解决:在配置类里加一个资源映射,把/uploads/**交给本地磁盘目录处理:
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Value("${file.upload-dir}") private String uploadDir; @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/uploads/**") .addResourceLocations("file:" + uploadDir + "/"); } }这段配置跑通之后,图片 URL 就是http://localhost:8080/uploads/xxx.jpg。注意${file.upload-dir}要是一个绝对路径,别用相对路径,否则换个工作目录启动就 404。另一个坑是前端把image_urls存成逗号分隔字符串,页面渲染前要用 split(',') 拆成数组,否则一个 image 标签会拿到一整串取不到的路径。
5.4 登录问题:一直提示“登录凭证校验失败”或“用户不存在”
现象:wx.login 拿到的 code 发给后端,后端换 openid 时返回失败。原因:通常有三类,一是后端配置的 AppID 和 AppSecret 与小程序开发者工具里打开的 AppID 不是同一套;二是服务器时间和微信服务端偏差过大,极少数情况导致 code 校验失败;三是同一个 code 被重复发送,code 是一次性的,第二次必然失败。解决:把后端 application.yml 里的 AppID 和密钥与开发者工具里的账号核对一遍。如果你用的是测试号,要确认后端的 secret 也对应测试号而不是正式号。开发调试时还有一个省事做法:后端加一个开发模式开关,当 code 等于某个预留值如test-code-001时,直接映射到固定 openid,这样不经小程序也能用 Postman 调全部接口。
5.5 接单后状态反复横跳:总有人“抢单成功”,过一会又变回待接单
现象:A 和 B 同时接同一单,前端都返回成功,过几分钟刷新列表,单子状态变了。原因:这就是前面 4.3 讲的并发漏洞,用 select 判断再加 update 的写法,两个事务都能通过判断。解决:把接单逻辑改成一条条件 UPDATE,影响行数为 0 时明明确确返回“需求已被接走”。同时前端要在收到失败后刷新列表并移除或置灰对应条目,不要在本地乐观地保留成功状态。这个坑不仅出现在接单,“取消订单”和“完成订单”同样要用带状态的 UPDATE 来保证只有当前状态正确的人能操作。
6. 答辩前夜:冒烟测试、数据库备份与演示兜底
在正式答辩前,我不会急着去开微信开发者工具,而是先用一组 curl 命令把后端整条链路跑一遍。这个习惯救过我很多次,因为页面报错有时会被前端掩盖,而直接请求接口能看到最原始的错误信息。下面这套流程也推荐你放在答辩前一晚。
6.1 全流程接口验证:从登录、发布到完成评价的 4 条 curl 命令
以本地后端为例,打开开发模式测试账号,用预留的 code 换取 token:
TOKEN=$(curl -s -X POST http://localhost:8080/api/auth/login \ -H 'Content-Type: application/json' \ -d '{"code":"test-code-001"}' | jq -r '.data.token') echo $TOKEN拿到 TOKEN 后,发布一条测试需求:
curl -s -X POST http://localhost:8080/api/order/save \ -H 'Content-Type: application/json' \ -H "token: $TOKEN" \ -d '{"title":"帮忙取快递-冒烟测试","type":0,"reward":1.00,"location":"3号宿舍楼"}'响应里会带回新建记录的 id,把这个 id 记下来,模拟另一个账号接单:
curl -s -X POST http://localhost:8080/api/order/accept \ -H 'Content-Type: application/json' \ -H "token: $TOKEN" \ -d '{"orderId": 1}'最后把单子完成并评价,确认整个状态机走通:
curl -s -X POST http://localhost:8080/api/order/finish \ -H 'Content-Type: application/json' \ -H "token: $TOKEN" \ -d '{"orderId": 1}' curl -s -X POST http://localhost:8080/api/order/comment \ -H 'Content-Type: application/json' \ -H "token: $TOKEN" \ -d '{"orderId": 1, "rating": 5, "content": "速度很快"}'每条返回的 code 都是 200 才算通过。这个测试脚本不要写在答辩现场,提前一天跑通,把响应样例截图存下来,这也是答辩 PPT 里的“接口测试”一页。注意 jq 不是所有机器都有,如果没有,改用python -c "import sys,json; print(json.load(sys.stdin)['data']['token'])"解析也行。
6.2 备份与迁移:mysqldump 导出比手动复制表更可靠
每次演示前我习惯做一次全量备份,特别是换机器演示的场景。不要在可视化工具里“导出表数据”,那种方式会把表结构拆碎,重建时容易漏索引。用命令行导出最稳妥:
mysqldump -u root -p --default-character-set=utf8mb4 campus_help > campus_help_$(date +%Y%m%d).sql把这份 sql 拷贝到演示机上导入:
mysql -u root -p --default-character-set=utf8mb4 campus_help < campus_help_$(date +%Y%m%d).sql提示:两台机器的 MySQL 大版本尽量一致,5.7 导出导入到 8.0 多数情况没问题,反向则可能因为默认字符集差异出现告警。导入前在目标库里先执行CREATE DATABASE IF NOT EXISTS campus_help DEFAULT CHARACTER SET utf8mb4;,再执行导入命令,能避免库不存在的报错。
6.3 演示兜底动作:预置数据与重启验证,把慌张留在今晚
答辩演示最怕的事,是打开页面时发现列表是空的。我给自己定了一个固定预置清单:登录账号保持已绑定学号状态;广场上预置一条待接单需求和一条已完成并带评价的单子;图片路径能正常访问。演示时按“登录-接预置信单-完成-评价”或“发布新需求-另一个账号接单-完成”两条主线之一走,不要上线时现编数据。
后端在演示前重新启动一次,并重新导入一次最新备份的 SQL,确认启动日志里没有红色报错。检查一下系统时间是否与当前时间一致,因为服务器时间和本机时间差超过几分钟,订单列表按时间倒序就会出现“新单在底下”的尴尬。我经历过一次最丢人的现场:所有接口正常,图片不显示,折腾半天发现后端服务器是另一个目录在跑,改的配置根本没生效。所以现在每次演示前,我都会看一眼控制台打印的工作根目录,再顺手敲一次接口确认状态。希望这些经验能帮你在答辩现场少踩一层坑,把精力留给“为什么这么设计”的价值输出。
本文还有配套的精品资源,点击获取