ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

滑雪场管理系统全栈实战:SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0

滑雪场管理系统全栈实战:SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0 滑雪场管理系统从零到一的 Java Web 全栈落地全记录做毕业设计或者自己练手最怕的不是没有代码而是拿到一堆源码却不知道从哪看起。之前同学问我借“滑雪场管理系统源码”的时候我给了他一整套 SpringBoot2 Vue3 MyBatis-Plus MySQL8.0 的项目结果他三天后又跑来找我说看不懂配置、连不上数据库、前端还报跨域错误。这种问题我见得太多了并不是代码有问题而是很多人根本不理解这套技术栈之间是怎么协作的。这篇我把当时做滑雪场管理系统的完整思路、技术选型、数据库设计、部署避坑全部摊开来讲。这既是一篇项目复盘也可以当作一套 Java Web 全栈开发的实战教程。不管你是准备做毕设、期末课设还是想完整走一遍 SpringBoot2 Vue3 前后端分离项目的开发流程这套滑雪场的业务都足够典型——它有用户、有订单、有计费、有复杂的状态流转能把你该练的技术点基本都覆盖到。1. 项目整体设计与技术选型思路1.1 为什么选 SpringBoot2 Vue3 这套组合前后端分离已经是现在 Java Web 开发事实上的标准做法而 SpringBoot2 Vue3 恰好是当前生态最成熟、资料最多、学习成本相对最低的组合之一。SpringBoot2 相比 SSH 时代的 XML 堆叠配置最大的优势在于“约定大于配置”。sqL比如你想集成 MyBatis-Plus只要在 pom.xml 里加一个依赖配置好数据源就能直接用 BaseMapper 提供的一堆 CRUD 方法。这对做过传统 SSM 的人来说简直是从手工搓方向盘变成了自动挡。而对新手来说SpringBoot 的自动装配机制虽然像个黑盒但它的好处在于你不需要理解每一个 Bean 的创建细节框架已经帮你把缺省配置全做好了。你只需要在 application.yml 里写清楚“连接哪个库、端口多少、开启哪个功能”SpringBoot 就会按照约定把相应的组件装配起来。Vue3 这端我用的组合式 APIComposition API加script setup语法。相比 Vue2 的 Options API组合式 API 的逻辑聚合能力强很多。以滑雪场管理系统为例滑雪票预订页需要同时处理票种选择、数量加减、价格计算、场次日历联动这四个业务逻辑在 Vue2 里你得分别写进 data、computed、methods、watch 四个地方隔了几个屏幕来回跳用 Vue3 的 setup你可以在同一个代码块里把“选票状态、计算逻辑、提交方法”全部组织在一起业务链路读起来就像一篇流水账一样顺。还有一个不能忽略的点现在很多管理系统都要求权限精细控制。比如滑雪场里普通用户只能看自己的订单和教练预约管理员才能查所有订单、管理雪具库存、审核退票。Vue3 配合 Vue Router 的动态路由守卫可以在前端把未登录用户拦截在登录页之外后端用 SpringSecurity 或者简单的拦截器做第二层校验。前后端双校验既保证体验也保证安全。1.2 业务场景梳理滑雪场管理系统到底要管什么滑雪场的核心业务一句话总结就是把人、雪票、教练、雪具、场地这几个资源在时间维度上编排好。听起来简单真正建模的时候会发现牵扯的状态很多。我最终把系统拆成了七个大模块用户模块游客注册、登录、个人信息维护区分普通用户和管理员两种角色。管理员账号一般在数据库初始化时直接插入。雪票管理管理员维护票种比如成人日场票、儿童平日票、夜场票、季卡每个票种有不同的价格、有效期、使用条件。用户在前端按场次购票生成订单。场地/场次管理滑雪场分初级道、中级道、高级道每条雪道在不同时段早场、日场、夜场的开放状态不同系统需要对每个场次的剩余接待量做限制。教练预约教练作为资源来管理有技能等级、可预约时段、单价。用户选择教练和时间段提交预约后进入待确认状态教练或管理员确认后生效。雪具租赁雪板、雪靴、头盔、护具等设备有库存数量租赁计时计费。归还时验收损坏和逾期都要走额外计费流程。订单中心统一个人用户的所有票务订单、租赁订单、教练预约记录支持状态查询和退订操作。数据统计管理员侧查看销售曲线、热门票种排行、教练预约量、当日收入等数据用图表展示。这种模块划分既贴合滑雪场的实际运营链路也正好覆盖了 Java Web 课程要求的增删改查、关联查询、状态流转、文件上传、权限管理等技术考核点。1.3 为什么 MyBatis-Plus 能显著减少重复劳动以前用原生 MyBatis每写一个实体类都要配套写一个 Mapper 接口再写一个 XML 文件里面塞满 idselectList 之类的 CRUD 语句。一个五六个表的小项目不觉得累一旦到了滑雪场这种十几张表的规模光写基础 SQL 就能消磨掉一半耐心。MyBatis-Plus 解决的就是这“一半的耐心”。它提供 BaseMapper 把单表 CRUD、分页查询、批量插入、逻辑删除全部内置了。比如你要查所有带“夜间场”关键词的票种LambdaQueryWrapperTicketType wrapper new LambdaQueryWrapper(); wrapper.like(TicketType::getName, 夜场).eq(TicketType::getStatus, 1); ListTicketType list ticketTypeMapper.selectList(wrapper);没用一行 SQL查询条件写起来像在写链式调用。更重要的是 LambdaQueryWrapper 用方法引用而不是字符串来指定字段编译期就能发现字段名拼写错误这在修改表结构时能省下大量排查时间。MyBatis-Plus 的分页插件也是选它一个很重要的理由。滑雪场后台的订单列表、用户列表动辄几千条原生 MyBatis 分页要么手写 LIMIT 自己算总条数要么引入 PageHelper。MyBatis-Plus 提供了一个内置的分页插件组件配置方式非常简单Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }配置完成后Service 层只需要返回分页结果PageTicketOrder page ticketOrderMapper.selectPage( new Page(pageNum, pageSize), new LambdaQueryWrapperTicketOrder() .eq(TicketOrder::getUserId, userId) .orderByDesc(TicketOrder::getCreateTime) );Page 对象里自带 records、total、current、size 四个属性前端拿到之后直接渲染表格和分页组件什么都要自己算的苦日子算到头了。2. 核心框架整合细节与关键配置2.1 SpringBoot2 工程初始化与依赖管理一个 SpringBoot2 MyBatis-Plus 的标准工程最核心的依赖其实就是那么几个。我直接用 Spring Initializr 生成基础工程然后手动补充依赖。parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.5/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies这里有一个值得注意的坑SpringBoot 的父工程管理了 MySQL 驱动的版本但它默认的 MySQL 驱动版本在 5.x 项目中能用在 MySQL8.0 下会有驱动类名和时区问题。所以 MySQL8.0 项目里建议在 properties 里显式指定一下版本properties mysql.version8.0.33/mysql.version /properties2.2 application.yml 配置数据源、MyBatis-Plus 与日志输出SpringBoot 项目的配置集中在 application.yml很多新手在这里栽跟头尤其是时区问题。我最终的配置长这样server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/ski_resort?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue username: root password: 你的密码 mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0逐个解释一下关键项driver-class-nameMySQL8.0 的驱动类是com.mysql.cj.jdbc.Driver不再是以前 5.x 的com.mysql.jdbc.Driver写错直接报 ClassNotFoundException。serverTimezoneAsia/Shanghai不设置时区驱动会把本地时间按 UTC 处理数据库里的时间和你系统时间会差 8 个小时。这个问题出现频率极高我见过好几个项目在部署时发现“订单创建时间比实际时间晚 8 小时”全是这里漏了。allowPublicKeyRetrievaltrueMySQL8.0 默认使用 caching_sha2_password 认证插件连接时驱动可能要求获取公钥这个参数能避免偶尔出现的连接失败。map-underscore-to-camel-case把数据库的user_name自动映射到实体类的userName这个必须开不然你写 MyBatis-Plus 的 LambdaQueryWrapper 时字段映射会出现一堆莫名其妙的问题。log-impl: 开发阶段开启 SQL 日志能在控制台直接看到 MyBatis-Plus 生成的 SQL 语句排查 JOIN 查询、条件拼接问题非常好用。上线前记得关掉。2.3 Vue3 工程搭建Vite 与前端目录设计前端我选了 Vite 而不是 Vue CLI。Vite 基于 ES Module开发服务器冷启动速度极快改代码热更新也是毫秒级响应。用过一次之后基本就不会想回 vue-cli 的 Webpack 方案了。创建项目npm create vitelatest ski-admin -- --template vue cd ski-admin npm install npm install vue-router4 pinia axios element-plus目录结构我按“功能 类型”双维度组织这是后台管理系统最常用的组织方式src/ api/ # 所有接口请求函数按模块拆文件 auth.js ticket.js order.js coach.js equipment.js assets/ # 静态资源 components/ # 公共组件UploadImage、Pagination等 router/ # 路由配置与守卫 stores/ # Pinia 状态管理 views/ admin/ # 管理端页面 user/ # 用户端页面 utils/ # axios 封装、日期处理等工具函数这里要重点说 axios 封装。前后端分离项目里前端每个请求都要带 token后端返回的响应结构也需要统一处理如果每个页面都直接调 axios代码会非常冗余。我封装了一个 request.js 工具核心部分是这样的import axios from axios import { ElMessage } from element-plus import router from ../router const request axios.create({ baseURL: /api, timeout: 10000 }) request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer token } return config }) request.interceptors.response.use( response { const res response.data if (res.code ! 200) { ElMessage.error(res.message || 请求失败) if (res.code 401) { router.push(/login) } return Promise.reject(new Error(res.message)) } return res }, error { ElMessage.error(error.message || 网络错误) return Promise.reject(error) } ) export default request统一封装的收益在后端返回 401未登录或 token 过期时特别明显所有页面只要请求到 401都会自动跳回登录页不用在每个页面单独写判断逻辑。2.4 前后端联调的关键Vite 代理配置前后端分离项目开发阶段最烦的跨域问题其实一个 proxy 配置就能解决。我在vite.config.js里这样配export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true, rewrite: path path.replace(/^\/api/, ) } } } })这个配置的作用是前端页面发请求给/api/xxxVite 开发服务器会把请求转发到http://localhost:8080/xxx同时把请求头 Origin 改成目标地址浏览器层面的跨域检查就绕过去了。很多新手前端项目里直接写http://localhost:8080/api/login这种绝对路径部署的时候改得焦头烂额还容易被跨域策略拦截。用相对路径/api 代理转发开发环境、生产环境都不用改代码整洁很多。生产环境让 Nginx 做同样的反向代理直接沿用这套 url 结构就行。3. MySQL8.0 安装适配与数据库设计实战3.1 MySQL8.0 安装的三个场景实操MySQL8.0 的安装是热搜词里出现频率最高的一个我在做部署的时候也专门整理过三个最常用的场景。Windows 用户用 ZIP 压缩包装是最干净、最好控制的方式。去官网下载 mysql-8.0.x-winx64.zip解压到一个纯英文路径下比如 D:\mysql-8.0.33手动新建一个 my.ini 配置文件[mysqld] basedirD:/mysql-8.0.33 datadirD:/mysql-8.0.33/data port3306 character-set-serverutf8mb4 default-authentication-pluginmysql_native_password然后用管理员身份打开命令行依次执行mysqld --initialize-insecure mysqld -install net start mysql这里解释两个关键点--initialize-insecure会初始化数据目录生成一个 root 密码为空的账号适合首次登录再修改密码default-authentication-pluginmysql_native_password是为了兼容一些旧版客户端和驱动SpringBoot 用的 MySQL8 驱动本身是支持 caching_sha2_password 的但如果你的工具、驱动版本比较旧换成 mysql_native_password 能省很多麻烦。LinuxCentOS用 yum 源安装的话直接配好官方源然后一键安装wget https://repo.mysql.com/mysql80-community-release-el7-3.noarch.rpm rpm -ivh mysql80-community-release-el7-3.noarch.rpm yum install mysql-community-server -y systemctl start mysqld注意 CentOS 上安装完会自动生成一个临时 root 密码在/var/log/mysqld.log里grep temporary password /var/log/mysqld.log拿到临时密码登进去之后第一件事就是改密码而且要按 MySQL8.0 的密码策略设成包含大小写字母、数字和特殊字符的强密码不然会一直报密码不满足复杂度要求的错误。Docker 跑 MySQL8.0是我最推荐的学习环境搭建方式没有之一。一条命令起来docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD123456 \ -e TZAsia/Shanghai \ -v /data/mysql:/var/lib/mysql \ mysql:8.0 \ --character-set-serverutf8mb4 \ --collation-serverutf8mb4_unicode_ci参数说明一下TZAsia/Shanghai解决时区-v挂载数据目录容器删了重建数据还在mysql:8.0镜像用了默认的 caching_sha2_password 认证SpringBoot2 最新的 MySQL 驱动8.0.33完全兼容不用额外改插件。3.2 数据库建模核心表结构与外键关系说明滑雪场管理系统的数据库我认为最核心的是五张表。每个人设计的字段可能略有差异我把我用过的结构整理在这里供你直接参考用户表sys_user字段名类型说明idbigint主键自增usernamevarchar(50)登录名唯一passwordvarchar(255)BCrypt 加密后的密码real_namevarchar(50)真实姓名phonevarchar(20)手机号roletinyint1普通用户2管理员statustinyint0禁用1正常create_timedatetime注册时间雪票表ticket_type字段名类型说明idbigint主键namevarchar(100)票种名称pricedecimal(10,2)原价discount_pricedecimal(10,2)优惠价valid_daysint有效天数total_countint发行总量stockint剩余库存statustinyint0停售1在售remarkvarchar(255)备注订单表ticket_order字段名类型说明idbigint主键order_novarchar(32)订单编号唯一user_idbigint下单用户ticket_idbigint购买的票种ticket_namevarchar(100)票种名称快照unit_pricedecimal(10,2)成交单价快照quantityint购买数量total_amountdecimal(10,2)总价statustinyint0待支付1已支付2已使用3已退票create_timedatetime下单时间pay_timedatetime支付时间订单表里保存ticket_name和unit_price这两个快照字段是我特别想强调的设计习惯。雪票名称和价格都是会变的——管理员一改价格历史订单如果只存一个 ticket_id 外键展示给用户看的订单金额就会跟着变。快照的意义在于订单生成那一刻的信息被永久定格后面无论票种怎么调价改文案用户的历史订单仍然准确。教练预约表coach_reservation字段名类型说明idbigint主键coach_idbigint教练用户IDuser_idbigint预约用户IDdatedate预约日期time_slotvarchar(20)时段如 09:00-10:30statustinyint0待确认1已确认2已完成3已取消remarkvarchar(255)备注雪具租赁表equipment_rental字段名类型说明idbigint主键equipment_idbigint雪具类型IDuser_idbigint租用用户quantityint数量start_timedatetime开始时间end_timedatetime预计归还时间actual_return_timedatetime实际归还时间total_pricedecimal(10,2)租金statustinyint0租赁中1已归还2超时违约设计这些表的时候有一个通用原则我反复强调能快照就要快照能冗余就适度冗余。比如订单表冗余了票种名称和单价租赁表冗余了单价和押金虽然破坏了一点所谓的“三范式”但换来了查询速度和业务逻辑的简单性。管理系统的并发量和数据量都没到需要极致优化的程度业务清晰、查询方便永远是第一位的。3.3 时区与编码问题MySQL8.0 项目必踩的坑MySQL8.0 刚上手有两个几乎每个人都会踩的坑我先帮你排掉。第一个是时区。MySQL8.0 的连接 URL 如果没有显式指定serverTimezone会有类似这样的报错The server time zone value йʱ is unrecognized or represents more than one time zone.解决方式就是前面配置里写的URL 加上serverTimezoneAsia/Shanghai。中文乱码的根源在于 MySQL 服务器、数据库、表、应用四层编码不一致所以创建数据库的时候也要显式指定 utf8mb4CREATE DATABASE ski_resort DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;utf8mb4 是 UTF8 的超集能完整存下四字节的表情符号和生僻字现在的新项目不要再用老的 utf8 了。第二个是密码认证插件。如果你连接 MySQL8.0 时遇到Public Key Retrieval is not allowed在连接 URL 后面加allowPublicKeyRetrievaltrue就能解决或者把鉴权插件改回mysql_native_password。我一般两个方案都做URL 加allowPublicKeyRetrievaltrue建用户时也顺手指定mysql_native_password双保险CREATE USER ski_user% IDENTIFIED WITH mysql_native_password BY YourPassword123!; GRANT ALL PRIVILEGES ON ski_resort.* TO ski_user%;4. 毕业设计视角怎么利用这套系统做出高质量的答辩4.1 从任务书到系统落地的完整路径搜索热词里出现频率很高的一个词是“任务书”。临近毕业季很多人在做“基于 Java Web 的 XXX 系统设计与实现”这种课题滑雪场管理系统只是其中一个例子。其实任务书这种东西本质上是让你“走完一遍软件工程的完整流程”而不是真的让你研发一个商业级产品。所以拿到任务书之后我建议按这个顺序推进第一步把业务流程画清楚。用一个简单的泳道图画清楚“用户—系统—管理员”三条线之间的交互。滑雪场的链路就是用户注册登录、选票下单支付、到店核销入场、教练预约排课、雪具租借归还这五条主线。泳道图画好了后面的数据库设计和代码实现基本就有了地图。第二步定界面数量。一个能拿得出手的毕业设计界面至少要有 15 到 20 个左右才显得完整。滑雪场管理系统我做了登录注册、首页轮播、票种列表、购票确认、订单中心、教练列表、预约日历、雪具租赁、个人中心、后台数据看板、票种管理、订单管理、教练管理、雪具管理、用户管理等十六个页面这个数量在答辩时基本够了做太多反而容易给自己挖坑。第三步按模块逐个实现。不要想着一次把所有功能做完。先做通一个“用户登录 票种列表 下单支付”的最小闭环跑通了再往这个闭环上加“教练预约”“雪具租赁”“后台管理”这些增量模块。每个模块做完都跑一遍测试避免最后一次性联调时问题扎堆改到崩溃。4.2 PPT 讲解与答辩展示的技术亮点提炼答辩时间通常只有 5 到 10 分钟怎样把项目亮点讲出来很关键。我会按照下面这条逻辑线来组织讲解先花一分钟介绍项目背景和业务痛点“滑雪场日常客流大、票种多、教练排课靠手写登记表急需一套线上预订与管理系统。”这句话点明项目存在价值。然后花两分钟讲技术栈的选型理由“前端用了 Vue3 的组合式 API 配合 Element Plus 组件库实现十分钟内的快速开发迭代后端用 SpringBoot2 的自动装配让服务端开发专注于业务逻辑MyBatis-Plus 将单表 CRUD 的重复工作量降低了六成以上。”这里技术项要讲成“为什么选它”而不是“我用了它”。接着是重头戏花三分钟演示核心流程。我给答辩用的示范流程是注册一个用户登录后购买一张“成人平日日场票”模拟支付前端不做真实支付只做状态流转然后预约一位高级教练再租赁一套雪具。这条流程一次性跑通了该系统最核心的业务能力就已经展示完了。数据的回写、状态的变化、订单的展示都在这一条流程里。最后留一分钟讲“改进与展望”比如“后续可以接入微信小程序”“接入真实第三方支付”“增加基于 Redis 的分布式锁防超卖”等给评委留下系统有完备性和扩展空间的印象。这里注意别说得太满给一两个具体可落地的点就够。4.3 二次开发扩展从毕设代码到项目的成长路径滑雪场管理系统做完之后很多同学问的最多的问题是“接下来学什么”。我的建议是不要急着换新技术栈先在这套系统上做三个方向的增量开发。第一个方向给订单系统加上 Redis 缓存防超卖。滑雪票有一种季卡发行量少、抢购集中高并发下库存容易超卖。用 Redis 的DECR操作来预扣库存只有扣减成功才允许下单再把最终结果同步回数据库。这个改造既能学到 Redis 的实际应用又是一个天然的答辩亮点。第二个方向引入 SpringSecurity 做细粒度权限管理。现在系统里只有“普通用户”和“管理员”两种角色权限判断靠拦截器完成。引入 SpringSecurity 之后可以做到“教练”角色只能看到和管理自己的预约订单“安全员”角色只能操作事故上报模块权限模型更细。这个改动对你的权限设计理解和框架使用深度都是一次很好的提升。第三个方向把前端接入 ECharts 做销售数据分析。现在后台只是简单的表格展示接入 ECharts 后把 MySQL 里的订单流水按日期聚合画出日销售额折线图、票种占比饼图、教练业绩对比柱状图。这个模块视觉效果好代码难度也不大对项目的“完成度观感”提升极为明显。5. 实战避坑安装部署常见问题速查这套系统从开发到部署最烦的其实不是业务代码而是一些“环境类”的问题。我把实际操作中踩过的坑整理成了速查表按问题现象 原因 解决方案列出来你在部署的时候直接对应排查就行。问题现象可能原因解决方案启动报Access denied for user rootlocalhost密码错误或 MySQL 用户权限不对用管理员重置密码ALTER USER rootlocalhost IDENTIFIED BY 新密码;然后FLUSH PRIVILEGES;启动报Unknown database ski_resort数据库还没创建先执行 SQL 建库或者CREATE DATABASE ski_resort DEFAULT CHARACTER SET utf8mb4;连接报Public Key Retrieval is not allowedMySQL8.0 默认认证插件导致JDBC URL 增加allowPublicKeyRetrievaltrue中文乱码字符集不一致建库指定 utf8mb4JDBC URL 加characterEncodingutf8检查前端 HTML 的 meta charset页面请求 404后端控制台无日志前端请求路径和后端 Controller 不匹配用浏览器 F12 看 Network 实际请求 URL和后端RequestMapping路径逐一比对前端报跨域错误前后端分离开发环境跨域用 Vite proxy 代理生产用 Nginx 反向代理登录后很快又提示未登录token 过期时间设太短或前端未存 token在 JWT 或自定义 token 中设置合理过期时间我一般设 24 小时确保前端请求头带上 Authorization时间差 8 小时时区未配置JDBC URL 加serverTimezoneAsia/ShanghaiMySQL 配置全局时区实际排查过程中有一个很有效的通用思路看到报错先别急着去网上搜先看完整异常堆栈。SpringBoot 的异常定位基本都指到具体哪一行根据行号往回找比关键词搜索高效得多。以前部署另一个项目的时候遇到过几次非常奇怪的现象同样的代码、同样的配置文件在一台电脑上跑得好好的在另一台上就登录不了。最后排查来排查去发现是 MySQL 用户的 host 权限设置不一样——一台是rootlocalhost另一台是root%。如果你的代码没问题建议按顺序检查MySQL 用户权限 - JDBC URL 参数 - 依赖版本 - 防火墙端口。这四个方向能覆盖掉九成部署期报错原因。另外记录一个比较冷门但很典型的坑如果你项目中同时引入了mybatis-plus-boot-starter和mybatis-spring-boot-starter会出现“Invalid bound statement (not found)”的错误。原因是你把两个 ORM 的配置信息混在一起了MyBatis-Plus 的 Mapper 扫描和 MyBatis 原生的 Mapper 扫描冲突。解决办法就是只保留 MyBatis-Plus 的 starter去掉另一个依赖。这个坑网上一些老教程里很容易踩到因为早期很多人把两个都加进去做对比测试没删干净。还有一个细节值得提一下——文件上传。滑雪场管理系统里教练头像、雪具照片都涉及图片上传。开发的时候前端把图片传到后端存到一个 uploads 目录然后通过一个静态资源映射暴露出去。这个方案在单机部署没问题如果后续要上云、容器化部署记得把上传目录挂载到对象存储比如用 MinIO 自建或者用阿里云 OSS不然 Docker 容器一重建图片全没了。我在做这个项目时还特意试过用 PathPattern 和 AntPathMatcher 两种路径匹配方式来配置静态资源发现 SpringBoot2.6 之后默认的路径匹配策略改成了 PathPatternParser如果你之前用过ant_matcher的配置方式要注意兼容性问题。好的做法是统一用 WebMvcConfigurer 实现Configuration public class WebConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/uploads/**) .addResourceLocations(file: System.getProperty(user.dir) /uploads/); } }这样在 Windows 和 Linux 上都能正常工作不会出现路径分隔符导致的 404 问题。最后讲讲我个人使用这套技术栈的感受。SpringBoot2 Vue3 MyBatis-Plus MySQL8.0可以说是在“效率”和“可维护性”之间找到了很好的平衡点。SpringBoot 管好了后端装配Vue3 优化了前端状态管理MyBatis-Plus 解决了重复 SQLMySQL8.0 提供了更健壮的数据存储这套组合没有什么特别炫技的用法但每一层都在帮你减少做低级重复劳动的精力消耗把时间留给真正的业务设计。对一个想完整掌握 Java Web 全栈流程、或者正在做毕业设计的开发者来说它是一条性价比极高的学习路径——代码量适中、技术覆盖面广、资料丰富、遇到问题基本都能搜到答案。如果你也想从零搭一个类似的管理系统不妨就拿滑雪场这个业务来练手。先画出业务流程图再建好数据库表然后后端跑通一个接口前端渲染一个页面最后把整条链路串起来。等跑完这一遍你会发现曾经觉得高不可攀的“前后端分离项目”其实也就是一层一层搭积木的事。
返回列表