简介一套基于SSM与微信小程序的健身主题Java毕业设计项目面向计算机专业学生、毕业设计者及前后端分离学习者。项目已通过导师认可答辩评审分九十七分在Windows10/11环境下调试运行自带完整部署说明下载后即可启动也适合课程设计。包内共九百七十个文件体积约五十八点七六MB覆盖后端Java业务代码、小程序页面、Vue管理后台、SQL数据库脚本、答辩PPT、开题报告、使用文档、演示视频另有多个bat脚本一键完成安装、运行与打包结构清晰便于对照学习。目前已有153人学习/下载。这套资源整合了从源码到演示的完整链路不仅提供可运行工程还能帮助理解SSM框架与微信小程序的业务整合方式可作为毕业设计蓝本或课设参考节省从零搭建的时间。1. 拿 SSM 微信小程序做健身管理系统为什么这包值得拆一遍如果你打开过十几个号称“下载即用”的毕业设计源码大概率遇到过这种场景代码解压完数据库脚本导入就报错Tomcat 一启动就端口冲突小程序端怎么都连不上接口。这套基于 SSM 微信小程序的健身管理系统后台是 Spring SpringMVC MyBatis 三件套前台是微信小程序围绕会员管理、课程预约、私教排期这三条主线把数据串起来。我把它当做一个能完整复现前后端分离毕业设计的样本适合做 Java 毕业设计、SSM 课程设计也适合想在小程序端把后端接口走通的初学者。答辩评审能到 97 分说明它在业务完整性和文档配套上确实做得比较齐。下面按照我实际拆包的过程把每一步和踩过的坑都写出来。2. 后端分层与接口设计表结构怎么落、JSON 怎么回拿到包先别急着导入 IDEA先花五分钟把后端工程结构认明白。SSM 项目的经典分层是 Controller 收参数、Service 管业务、Mapper 管数据库中间再用实体类把数据传下去。这套健身系统的核心业务并不复杂难点反而在“课程预约”和“会员信息”这两块怎么把表设计得合理。2.1 核心表设计会员、教练、课程、预约一条线健身类系统的表一般绕不开四张主表加几张字典表。第一张是用户表用来存管理员、教练、会员三种角色靠一个role_type字段区分第二张是课程表记录课程名称、所属教练、上课时间、人数上限第三张是预约表把用户和课程关联起来用于判断是否重复预约第四张是会员卡表记录开通时长和到期时间。实际建库时我会先把四张表的主外键关系画清楚再动手避免后面写 JOIN 查出全是笛卡尔积。资源里如果带.sql脚本通常会有这样的建表语句我挑出核心部分加注释方便你对照CREATE DATABASE IF NOT EXISTS fitness_ssm DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci; USE fitness_ssm; CREATE TABLE sys_user ( user_id INT AUTO_INCREMENT PRIMARY KEY COMMENT 用户ID, openid VARCHAR(64) DEFAULT NULL COMMENT 小程序openid, user_name VARCHAR(32) NOT NULL COMMENT 登录名, password VARCHAR(64) NOT NULL COMMENT 登录密码, real_name VARCHAR(32) DEFAULT NULL COMMENT 真实姓名, role_type TINYINT NOT NULL DEFAULT 2 COMMENT 0管理员 1教练 2会员, phone VARCHAR(20) DEFAULT NULL COMMENT 手机号, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_user_name (user_name) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; CREATE TABLE course ( course_id INT AUTO_INCREMENT PRIMARY KEY, course_name VARCHAR(64) NOT NULL, coach_id INT COMMENT 教练ID关联sys_user, course_date DATETIME COMMENT 上课时间, duration_min INT DEFAULT 60 COMMENT 单节时长(分钟), capacity INT DEFAULT 20 COMMENT 人数上限, enrolled_count INT DEFAULT 0 COMMENT 已报名人数, course_status TINYINT DEFAULT 0 COMMENT 0未开始 1进行中 2已结束 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT健身课程表; CREATE TABLE course_booking ( booking_id INT AUTO_INCREMENT PRIMARY KEY, course_id INT NOT NULL, user_id INT NOT NULL, book_time DATETIME DEFAULT CURRENT_TIMESTAMP, status TINYINT DEFAULT 0 COMMENT 0已预约 1已取消, UNIQUE KEY uk_course_user (course_id, user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT课程预约表;这里有一个容易被新手忽略的设计点course_booking给(course_id, user_id)加了唯一索引它的作用是让数据库层面拦截重复预约而不是只靠代码里if判断。如果只在前端做校验用户连点两次按钮、或者两台设备同时提交就会出现重复记录。加这个唯一索引属于“防重复提交”的兜底手段。course.capacity和enrolled_count记录人数上限和已报名数下单成功后再给enrolled_count加 1这就构成了最朴素的库存扣减逻辑。2.2 三层调用链Controller 到 Service 再到 Mapper 传什么看代码时会发现每个功能都是“Controller 收参 → Service 处理 → Mapper 查库”的一条链路。以课程列表为例查询条件一般有课程名、课程状态、上课日期范围这些条件不要用散乱的单个参数往方法里塞并成一个查询对象传下去会清爽很多。下面是课程列表接口的 Controller 写法用的是 RESTful 风格RestController RequestMapping(/api/course) public class CourseController { Resource private CourseService courseService; GetMapping(/list) public Result list(CourseQuery query) { // query 由前端以 Query String 传入SpringMVC 自动绑定到对象属性 ListCourseVO list courseService.selectCourseList(query); return Result.success(list); } PostMapping(/book) public Result book(RequestBody BookForm form, HttpSession session) { // 用户ID优先从 session 取不要轻信前端传的 userId Integer userId (Integer) session.getAttribute(userId); if (userId null) { return Result.error(401, 请先登录); } boolean ok courseService.bookCourse(userId, form.getCourseId()); return ok ? Result.success(null) : Result.error(人数已满或课程已开始); } }RestController是Controller加ResponseBody的合并注解意味着每个方法直接返回 JSON不再走 JSP 视图解析器如果项目里配了InternalResourceViewResolver记得确认它对/api路径没有拦截。CourseQuery这个对象我一般放在vo包里字段名和前端传参名保持一致SpringMVC 才能绑定进去。值得注意的一个细节是list(CourseQuery query)没有加RequestBody因为 GET 请求的参数走 url 拼参加了反而绑不上POST 提交的BookForm是 JSON 体必须加RequestBody。这个区别经常让人踩坑。真正的动态 SQL 会落在 Mapper 的 XML 文件中MyBatis 这一层决定了 SQL 能不能按条件灵活组合我一般会在 XML 里写成这样select idselectCourseList parameterTypecom.fitness.vo.CourseQuery resultTypecom.fitness.entity.Course SELECT c.course_id, c.course_name, c.course_date, c.duration_min, c.capacity, c.enrolled_count, c.course_status FROM course c where if testcourseName ! null and courseName ! AND c.course_name LIKE CONCAT(%, #{courseName}, %) /if if testcourseStatus ! null AND c.course_status #{courseStatus} /if if testbeginDate ! null AND c.course_date gt; #{beginDate} /if /where ORDER BY c.course_date ASC /selectwhere标签会自动处理多余的前置AND/OR第一个条件成立时不会在 WHERE 前面多出 AND这是 MyBatis 自带的智能拼接。test里面写的是 OGNL 表达式courseName ! null and courseName ! 这个写法要求CourseQuery对象里有对应属性的 getter否则会直接抛ReflectionException。#{courseName}是预编译占位符最终生成?占位由 JDBC 的PreparedStatement处理能挡住大部分 SQL 注入如果图省事用${courseName}拼字符串等于把参数直接拼进 SQL这是我在代码审查里一定会拦下来的写法。日期比较用gt;是因为 XML 里裸写会被解析成节点结束符要么用转义要么把比较条件放在![CDATA[ ]]里。2.3 返回格式与登录态前后端协商一个 Result 类微信小程序端不是浏览器没有 cookie 机制SSM 后端如果继续用HttpSession维持登录态小程序端每次请求都需要显式带上会话标识。这套系统用了最朴素的方式小程序端wx.login拿到 code后端拿着 code 向微信接口换 openid再把 userId 存进 session小程序端把后端返回的 token 放进 header 里的Authorization后端的拦截器从 header 里解析用户身份。所有接口的返回体建议统一成Result对象而不是让每个方法自己拼 JSON不然前端出 bug 时排查成本很大。常见的约定是public class Result { private Integer code; // 200 成功401 未登录500 业务异常 private String msg; // 给前端提示文案 private Object data; // 真正的业务数据 public static Result success(Object data) { Result r new Result(); r.code 200; r.msg OK; r.data data; return r; } // error 静态方法省略业务里按场景调用 }小程序端的wx.request在success回调里第一件事就是判断res.data.code是否为 200而不是直接渲染res.data。把code、msg、data三层分清楚之后无论是课程列表、登录接口还是下单接口前端只用写一套通用处理逻辑就够了。记住这个约定后面读小程序端代码会快很多。3. 微信小程序端从首页渲染到预约下单的完整链路管理端跑通后再来看小程序端。这一层如果你没写过小程序最需要先建立的概念是小程序没有 DOM 操作页面由wxml wxss js三件套组成所有数据绑定靠setData。这也意味着你没法像在浏览器里那样直接document.getElementById一切渲染都走数据驱动。3.1 请求封装与本地配置把 wx.request 包成 Promise原生wx.request是回调风格页面多了之后会出现“回调地狱”。所以拆这个包时先看utils/request.js它通常会把wx.request封装成 Promise并且统一在 header 里带上 token。一个最常用的形式是这样const BASE_URL http://localhost:8080/fitness; const request (url, method GET, data {}) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL url, method: method, data: data, header: { Content-Type: application/json, Authorization: wx.getStorageSync(token) }, success: (res) { if (res.data.code 200) { resolve(res.data.data); } else if (res.data.code 401) { wx.navigateTo({ url: /pages/login/login }); reject(new Error(未登录)); } else { reject(new Error(res.data.msg)); } }, fail: (err) { reject(err); } }); }); }; module.exports { request, BASE_URL };这里的BASE_URL是全局配置开发者工具里填http://localhost:8080没问题但真机预览时必须改成电脑的局域网 IP例如http://192.168.1.100:8080否则手机连不上。同时在微信开发者工具的详情设置里勾上“不校验合法域名”否则工具会拦截所有http请求。这两点几乎是新手遇到“请求 fail 或者一直转圈”的头号原因。提示真机调试改 BASE_URL 为局域网 IP 时记得把后端服务的 8080 端口在防火墙中放行否则手机能 ping 通电脑也照样访问不了。wx.getStorageSync(token)是从本地缓存同步读取 token每次请求自动带上。后端拦截器拿到Authorization后去 session 里查对应用户查不到就返回 401前端这边就统一跳登录页。这里就引出登录的问题了。3.2 登录流程与首页数据渲染小程序登录现在通用的流程是wx.login拿 code而不是像网页端那样直接输入用户名密码。拿到 code 之后调用后端接口后端的LoginController大致处理过程如下PostMapping(/wx/login) public Result login(RequestBody WxLoginForm form) { // 微信 code 只能使用一次且有效期五分钟 String openid wxService.code2Openid(form.getCode()); if (openid null) { return Result.error(code 无效或已过期); } User user userService.findOrCreateByOpenid(openid); String token UUID.randomUUID().toString().replace(-, ); sessionCache.put(token, user.getUserId()); // 生产环境换成 Redis return Result.success(token); }code2Openid需要后端配好appId和appSecret这两个值在微信公众平台的“开发管理 → 开发设置”里拿不能写死在小程序前端代码里否则等于把密钥公开。用ConcurrentHashMap做 session 缓存是毕业设计阶段的常见做法能跑通但服务重启后 token 全部失效上生产环境时换成 Redis 并设置过期时间才是合理方案。这个接口返回的 token 就是 3.1 里 header 里带的东西。首页渲染逻辑就简洁了课程列表页在onLoad里调用request(/api/course/list)拿到数组后setData给wxml的wx:for。课程卡片一般要展示课程名、教练名、时间、剩余名额数据不够时后端多半会返回一个CourseVO而不是原始实体类VO里已经 JOIN 出了教练姓名。你排查“页面上教练名空白”时可以优先检查接口返回里有没有coachName字段而不是去看wxml的绑定写法。对应的页面结构一般是这样的注意wx:key必须绑定唯一字段view classcourse-card wx:for{{courseList}} wx:keycourseId view classcourse-name{{item.courseName}}/view view classcourse-meta教练{{item.coachName}}/view view classcourse-meta{{item.courseDate}} · {{item.durationMin}}分钟/view view classcourse-meta已报名 {{item.enrolledCount}} / {{item.capacity}}/view button sizemini>async onBookTap(e) { const courseId e.currentTarget.dataset.id; const course this.data.courseList.find(item item.courseId courseId); if (course.enrolledCount course.capacity) { wx.showToast({ title: 课程已满, icon: none }); return; } const data await request(/api/course/book, POST, { courseId }); wx.showToast({ title: 预约成功, icon: success }); this.reloadList(); // 重新拉课程列表刷新已报名人数 }前端判断名额只是体验上的提前拦截真正的并发控制要放在后端。我在写这类接口时习惯用一条原子更新做兜底UPDATE course SET enrolled_count enrolled_count 1 WHERE course_id #{courseId} AND enrolled_count capacity同一时刻只有一个事务能成功更新即便前端连点两次按钮course_booking表的唯一索引也会拦下第二次插入接口返回“重复预约”的业务提示。发布会现场有人连续点按钮反而能展示出你对边界情况的考虑。3.4 手机号获取的限制与自定义导航栏高度微信小程序“获取手机号”这几年改过规则getPhoneNumber已经不是无条件可用的了涉及企业主体认证和平台收费策略个人主体的毕业设计项目经常拿不到完整手机号。这套健身系统如果只是做会员信息的展示建议不要强依赖手机号注册时用wx.login的 openid 作为账号标识手机号字段让用户手动填写即可。从这几年的规则变化看门槛一直在收紧答辩演示时如果这里报错评委比你还清楚原因所以预留“手动录入手机号”的入口才是稳妥方案。另一个刚需是自定义顶部导航栏的高度。如果项目里用了navigationStyle: custom顶部刘海屏会造成内容被遮挡常见做法是读取wx.getMenuButtonBoundingClientRect()拿到胶囊按钮的位置再结合wx.getSystemInfoSync()的statusBarHeight算出导航栏总高度把padding-top动态绑到页面上。这段逻辑最好写成公共方法放进utils因为每个页面都要用不同机型的状态栏高度不一样千万别写死成一个数字。4. 从压缩包到能跑目录识别、环境版本与三套批处理脚本后端和小程序端的代码逻辑通了接下来是最容易翻车的一步——部署。毕业设计源码最常见的通病不是代码写错而是环境版本对不上、数据库导入顺序搞错、端口被占用。这一节我按“先认目录、再配环境、最后跑脚本”的顺序带你过一遍。4.1 拆包后先看目录哪些文件要读、哪些文件别乱删解压后先别双击1-install.bat先看一眼根目录。一般会看到几个 README 或 doc 文档、数据库脚本目录、后端工程目录、小程序前端目录。这套资源里还出现了main.css.bak、update-password.vue.bak、IndexAsideStatic.vue.bak这类带.bak后缀的文件它们是开发过程中改版前留下的备份不是运行必需文件。正常部署时应该以不带.bak的原始文件为准不要因为“备份版本看起来更新”就把.bak改成原名覆盖现有文件很多人就是在这里把能跑的项目改坏的。如果根目录有pom.xml说明后端是 Maven 工程如果只有.classpath和.project说明是 Eclipse/MyEclipse 直接导入的动态 Web 项目。.classpath里能看到依赖的 jar 包路径用 IDEA 导入时选 “Import Project” 后让它识别 Maven 或 Eclipse 结构而不是手动把源码文件夹拖进去否则编译时 Tomcat 找不到Servlet类。管理端的前端文件这里出现了.vue后缀的备份说明它并不是单纯的 JSP 项目而是内嵌了一个 Vue 管理端。Vue 资源最终要npm install npm run build生成dist目录再由后端承接静态页如果只改了.vue没重新构建浏览器看到的永远是旧页面这一点是我拆这类包时常遇到的隐藏问题。4.2 环境版本怎么配JDK、Tomcat、MySQL 对照这套资源在 Win10/11 下调试通过我的建议是优先复现它的原始环境版本而不是手边有什么用什么。SSM 项目最常见的组合是下面这套兼容性也最稳组件推荐版本说明JDK1.8SSM 老项目兼容性最好Tomcat8.5对应 javax.servlet不是 jakartaMySQL5.7驱动路径com.mysql.jdbc.DriverMaven3.6拉依赖与打包如果你本机已经装了 MySQL 8.0不用卸载重装按第 5.1 节调整驱动类和 URL 参数即可。配置 Tomcat 时先确认CATALINA_HOME指向的版本再确认 IDEA/Eclipse 里的Server Runtime Environment选的是同一个 Tomcat。如果代码里用了javax.servlet而不是jakarta.servlet说明是 Tomcat 8/9 而不是 Tomcat 11这个版本错配会让项目启动时直接报NoClassDefFoundError。JDK 同理项目编译级别如果是 1.8就不要用 JDK 17 去跑语法上勉强能过但一些反射和代理相关的老框架会在运行时出问题。4.3 数据库初始化与数据源参数数据库是 SSM 项目最容易出错的一环。先把docs/sql或db目录下的.sql脚本用命令行导入而不是在 Navicat 里双击打开再手动复制因为脚本里如果有建库语句命令行执行能保证字符集和排序规则一致。我的习惯是这样mysql -u root -p fitness_ssm docs/sql/fitness_ssm.sql注意导入 SQL 之前先把 root 密码确认好脚本里的密码和本机不一致会直接中断最常见的报错是Access denied for user rootlocalhost。如果脚本里已经写了CREATE DATABASE那直接导入即可如果脚本里只有建表语句先CREATE DATABASE fitness_ssm DEFAULT CHARACTER SET utf8mb4;再切库导入。导入完成后重点查三张表的记录数比如sys_user里有没有默认管理员账号、course里有没有课程测试数据如果一张表是空的说明脚本可能没导全或者导错了库。数据源配置一般写在jdbc.properties或db.properties里修改后不需要重新编译 Java 代码重启 Tomcat 即可生效。一个典型的配置长这样jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/fitness_ssm?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password你的密码characterEncodingutf8负责让 JDBC 连接使用正确的字符集serverTimezoneAsia/Shanghai解决 MySQL 8.0 默认时区慢 8 小时的问题。useSSLfalse是在本地调试省去证书校验上生产再按运维要求开。这三项是 SSM 连接 MySQL 时最常被改的参数改完记得重启。4.4 install/run/build 三个脚本到底做了什么资源里带的1-install.bat、2-run.bat、3-build.bat看名字像一套安装流水线但实际执行前必须打开看一眼内容因为不同类型的工程这三个脚本干的事情完全不同echo off rem 1-install.bat 示例初始化数据库 set DB_USERroot set DB_PASS123456 mysql -u%DB_USER% -p%DB_PASS% docs\sql\fitness_ssm.sql echo install done pause如果后端是 Maven 工程install 脚本更常见的是mvn clean install -DskipTests执行完会往本地仓库塞依赖并打出 war 包。注意一点先确认脚本里写的数据库密码和你本机 root 密码是否一致脚本跑失败九成是密码不对或者mysql命令不在 PATH 里。2-run.bat一般负责启动 Tomcat常见写法是这样的echo off set CATALINA_HOMEC:\apache-tomcat-8.5.88 set JAVA_OPTS-Xms256m -Xmx512m -Dfile.encodingUTF-8 call %CATALINA_HOME%\bin\startup.batJAVA_OPTS里的-Dfile.encodingUTF-8值得保留它能让 Tomcat 在 Windows 上输出日志时不出现乱码-Xms256m -Xmx512m控制堆内存上下限几十人的毕设项目这个值足够。如果不想让 Tomcat 弹新黑窗口也可以直接把后端打成 war 包丢进webapps目录。3-build.bat通常是给前端 Vue 工程用的执行npm install、npm run build。但你要注意npm命令依赖 Node.js 环境如果你的机器没装 Node 或者版本太旧这个脚本会直接报npm 不是内部或外部命令。前端构建产物dist拿到之后要么让后端直接承接静态资源要么单独部署到 Nginx这一步决定了小程序端和管理端能不能同时访问到同一个后端。先跑通1和2再处理3是我推荐的顺序。5. 部署避坑与排查五条踩坑记录的现象、原因、解法这一章是我拆这类资源时的血泪经验。每一条我都按“现象 → 原因 → 解决”的格式写你遇到时可以照顺序排查。5.1 MySQL 8.0 驱动与时区报错现象Tomcat 启动后打开页面提示Cannot create JDBC driver of class com.mysql.jdbc.Driver或控制台刷出大段Server returns invalid timezone. Go to Advanced tab and set serverTimezone。原因项目写死在jdbc.properties里的驱动类是com.mysql.jdbc.Driver这是 MySQL 5.x 时代的驱动路径MySQL 8.0 把驱动类改成了com.mysql.cj.jdbc.Driver并且要求 JDBC URL 里显式声明时区。解决驱动类改成com.mysql.cj.jdbc.DriverURL 末尾追加serverTimezoneAsia/Shanghai。如果数据库密码包含特殊字符比如或#记得在.properties里用\转义或改用 URL 编码形式否则密码会被截断一直报Access denied for user。5.2 数据库导入后所有中文变成问号现象管理端页面、小程序列表里凡是中文都显示成????但英文和数字正常。原因大概率是建库时没指定字符集或者导入脚本里没有SET NAMES utf8mb4导致数据库实际用 latin1 存中文写入时字符无法完整编码。解决最彻底的方案是重建数据库建库语句写成CREATE DATABASE fitness_ssm DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;然后命令行导入前先执行SET NAMES utf8mb4;。如果数据已经写进去了先别急着删表用ALTER TABLE sys_user CONVERT TO CHARACTER SET utf8mb4;转换但已损坏的??数据通常是找不回来的所以毕业设计阶段直接重建库最快。5.3 前端页面白屏查了半天发现是 .bak 文件替换错了现象管理端登录页打开白屏F12 控制台报Failed to load module script或Unexpected token 也有人误删.vue源文件后构建失败。原因包里的main.css.bak、update-password.vue.bak、IndexAsideStatic.vue.bak是旧版本备份有人为了“更新一下前端”把.bak后缀去掉后覆盖了正式文件导致 Vue 工程里出现重复或残缺定义或者直接把.vue文件误删npm run build自然失败。解决部署前把.bak文件连同其对应关系列个清单确认哪些是运行时必需文件哪些只是备份。改动任何前端文件之前先把当前可运行版本整体复制一份作为回退点改坏了还能回到原状“后悔药”手里先备好。白屏问题解决后重新构建dist再部署浏览器强制刷新一次排除旧缓存干扰。5.4 小程序真机预览请求全部失败localhost 连不上现象开发者工具里调试正常一扫码到真机上所有接口就request:fail或者页面一直在转圈。原因真机上没有“localhost”这个概念它指向的是手机本身后端在电脑上必须用电脑在局域网内的 IP 地址才能访问。另外微信开发者工具如果没勾选“不校验合法域名”工具会拦截所有不带 HTTPS 的请求。解决把utils/config.js里的BASE_URL改成http://电脑局域网IP:8080/fitness手机和电脑连同一个 Wi-Fi关闭电脑防火墙的入站拦截或放行 8080 端口。如果后端加了CrossOrigin仍然报跨域检查小程序端请求头里的Content-Type是不是application/json触发了预检请求SSM 端的跨域过滤器要同时支持OPTIONS方法。小程序正式发布时后端接口必须上 HTTPS 并配置合法域名这个话题在微信公众平台有专门指引答辩演示阶段用开发者工具即可。5.5 Tomcat 端口被占用或启动两次失败现象2-run.bat双击后窗口一闪而过看日志发现Port 8080 required by Tomcat v8.5 Server at localhost is already in use。原因前一次启动没有正常关闭或者电脑上其他服务占用了 8080。毕设机器装过多个 Tomcat、装过 Nginx 的情况下尤其常见。解决先执行netstat -ano | findstr 8080看哪个 PID 占用端口在任务管理器里结束对应进程或者直接改server.xml里的三个端口号Connector port8080 .../以及 8005、8009 两个辅助端口。改完端口后jdbc.properties不用动但小程序端的BASE_URL要同步改。这里补充一条判断标准startup.bat启动后看到Server startup in xxx ms才算成功只显示Starting不代表启动完成这种模糊状态最容易误导人。6. 验证与扩展让演示更稳的接口自检清单6.1 二次开发建议三个好做又容易讲清楚的方向在这套系统上做二次开发性价比最高的有三个方向。第一个是预约提醒小程序端接入订阅消息用户预约成功时推送上课提醒这对健身类系统非常合理也绕开了手机号获取的坑。第二个是密码存储升级把sys_user表里的明文密码改成 BCrypt 加密登录时用BCryptPasswordEncoder校验这是答辩里安全性的加分项。第三个是首页数据可视化把每周课程预约量按天聚合出来用图表库画成折线图数据直接从course_booking.book_time分组查询即可工作量不大但演示效果很直观。6.2 验证顺序发布演示前固定走一遍的检查清单部署完成后下一步不是写论文而是把接口过一遍。打开浏览器输入http://localhost:8080/fitness/api/course/list如果能直接看到 JSON 数组说明后端和数据库这条链路已经通再用微信开发者工具重新编译小程序登录、首页列表、预约这三个页面走一遍。预约功能建议当场演示“重复预约被拦截”——第二次点击同一门课时收到“请勿重复预约”的提示这比顺滑的下单流程更能体现你在业务边界上的思考。验证时我固定过一张清单第一数据库服务是否启动用户名密码与jdbc.properties是否一致第二Tomcat 日志有没有Server startup第三管理端页面是否加载新构建而不是旧缓存第四小程序端BASE_URL在当前网络环境下能否被手机访问第五预约后重新查询课程列表enrolled_count是否加 1。这五项全绿演示翻车的概率就低得多了。资源包里的 PPT、开题报告和使用文档是答辩直接可用的材料但我的建议是把它当成参考提纲把你自己动手部署的过程、改过的参数和踩过的坑补进去这份材料会显得扎实很多。从那以后我每次拿到一个毕业设计压缩包第一件事永远是先跑部署脚本、确认环境再动源码顺序一旦反过来一晚上就没了。希望你这次部署顺利希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?