拓冰建站拓冰建站
首页 / 资讯中心 / 正文

SpringBoot2+Vue3旅游网站实战:从数据库设计到部署避坑全解析

新疆喀什我盯上这个地方很久了。不是因为烤包子和艾提尕尔清真寺而是因为喀什作为南疆旅游的核心集散地旅游信息展示、线路预订这些需求天然适合做一个Java Web业务系统来练手。当时手上正好有几个学生问我能不能带着做一套旅游网站类的毕设题目我给出的方案就是标题里这套SpringBoot2 Vue3 MyBatis-Plus MySQL8.0前后端分离含完整开发文档。这既是当前Java Web方向最主流的组合又是能覆盖前端、后端、数据库、部署全链路的实战项目更适合拿来做毕业设计或者入门企业级开发。这篇博文我就把整个项目的设计与实现思路完整拆一遍包括表结构、接口设计、核心代码、部署流程和避坑记录想复现的同学直接照着做即可。这个系统本质上是一个面向游客和景区运营方的信息平台游客端能浏览景点、筛选旅游线路、查看攻略、下单预订运营方通过后台管理景点、酒店、线路和订单。相比传统“一张网页挂几个景点照片”的静态站点这套系统有完整的用户体系、权限控制和业务数据流复杂度刚刚卡在“能体现开发能力”和“不至于做不完”之间。所以如果你是准备做Java Web毕设或者想往SpringBoot后端方向找工作但手里缺少能写在简历上的完整项目这套题目的价值相当高。1. 项目整体设计与思路拆解1.1 这套技术栈为什么能撑起一个完整的旅游网站先说项目选型的逻辑。SpringBoot2作为后端基础框架它的核心价值在于“约定优于配置”内嵌Tomcat、自动装配Starter、yml配置驱动这些特性让一个Web项目的骨架搭建时间大幅缩短。Vue3负责前端页面配合Vite构建工具和Element Plus组件库能快速实现景点列表、线路卡片、后台管理表格这类常见界面。MyBatis-Plus在MyBatis之上做了增强单表CRUD、分页、条件构造器可以直接调用比手写XML的效率高很多同时又比JPA更贴近Java后端面试常问的SQL优化场景。MySQL8.0作为存储层稳定、免费、生态文档多是这套系统的合理底座。这套组合必须是前后端分离的架构。后端只提供RESTful API前端通过Axios发请求拿JSON数据渲染页面前后端通过接口文档或者Swagger约定字段。这样做的直接好处是开发和调试可以并行页面和后端逻辑的耦合度低。另外很多同学在课程设计里做的是Thymeleaf模板渲染的老式单体应用那种方案虽然也能交差但在面试时没法回答“跨域”“JWT鉴权”“Axios拦截器”这类问题。用这套技术栈做完项目简历上能写的东西会多很多。1.2 需求边界在哪里什么是“够用”什么是“过度设计”很多人在设计旅游网站时容易犯一个毛病把功能堆得很满恨不得论坛、直播、在线支付全塞进去。结果开发周期拖长代码质量一塌糊涂毕业论文讲到一半讲不清楚。我当时给这个项目划定的是一个“业务闭环但边界克制”的功能集也就是用户能注册登录、能浏览查询、能下单、管理员能维护数据和审核订单整条链路是通的但不做支付对接、不做复杂的库存系统、不做多商户入驻。具体模块划分是这样的。游客端首页展示推荐景点和热门线路、景点列表支持分类筛选和关键词搜索、线路详情含行程安排和价格日历、酒店列表、旅游攻略文章、个人中心订单列表、收藏、个人信息。管理端用户管理、景点管理、线路管理、酒店管理、攻略管理、订单管理、数据看板。其中后台和前台的数据是同一套数据库通过角色权限控制访问范围RABC模型用一张用户表和一张角色表、一张用户-角色关联表实现就够了不要引入框架级的Security配置把项目复杂度拉上去。1.3 前后端分离架构下的请求流转过程我建议拿到题目后先画一张请求流转的图不用画得很正式草稿即可浏览器输入网址访问Vue打包出的静态页面用户点击按钮后Axios发起HTTP请求到后端接口http://localhost:8080/api/xxxSpringBoot的Controller接收到请求调用Service层业务方法Service通过MyBatis-Plus的Mapper接口操作数据库数据返回后以JSON格式响应给前端前端拿到数据更新响应式变量Vue的虚拟DOM完成页面刷新。真正写代码的时候你需要让前端所有请求统一走一个Axios实例加上baseURL、超时时间、请求拦截器从localStorage取token并加到请求头、响应拦截器统一处理后端返回的code和msg。这样请求链路里的鉴权和错误提示就是全局的了不会出现一个接口一个样。后端的统一返回结构我用的是{code: 200, message: success, data: ...}Controller层不直接返回裸对象而是包一层Result类前端拦截器拿它来判断业务是否成功。这一步做好了联调时能省很多事。2. 核心技术选型解析每个组件为什么这么选2.1 SpringBoot2选择版本时不要盲目追新这里必须提一个实际经验框架一定不要选最新的大版本。当前阶段SpringBoot3.x已经出了但它基于Java17和Jakarta EE命名空间很多老教程、三方Starter的包名和用法对不上。毕设和普通项目用SpringBoot2.7.x是最稳的网上能找到的案例、资料、踩坑贴最多遇到问题搜索一下就有答案。而且对mysql-connector-java、mybatis-plus-boot-starter这些依赖库来说2.x分支的兼容性验证是最充分的。SpringBoot2在这个系统里的核心作用体现在三个层面。第一是自动装配引入spring-boot-starter-web之后SpringMVC、内嵌Tomcat、JSON序列化组件会自动配置好不需要写繁琐的XML。第二是配置体系的简化所有环境相关配置收敛到application.yml数据源、端口、MyBatis-Plus逻辑删除配置一目了然。第三是生态整合配合spring-boot-starter-validation做参数校验、配合lombok减少样板代码项目的代码量至少能压缩三成。我建议新建项目的依赖清单固定为web、validation、mysql-connector-java、mybatis-plus-boot-starter、lombok、jwt鉴权用、hutool工具类不要加多余的东西。2.2 Vue3 Element Plus前台体验与后台效率的平衡Vue3相比Vue2最大的变化就是Composition API也就是把某个功能相关的变量和方法写在一个setup函数里代码的组织方式从“按选项类型分”变成了“按业务逻辑分”。这个旅行网站里最典型的例子是景点列表页搜索关键词、筛选分类、分页参数、加载状态、列表数据这几样东西在Vue2里要分散到data、methods、computed三个区域而在Vue3里可以全部塞到一个useSpotList函数里逻辑内聚度明显更高。虽然setup写多了代码前期会不太习惯但习惯了之后是真的不想再回去写options API。Element Plus组件库配Vue3是天作之合。后台管理页面里的表格、表单、弹窗、分页、级联选择器全部有现成组件最花时间的数据展示层直接组合一下即可。前台游客页面我建议自己写部分样式否则一眼就能看出是管理后台改的缺乏旅游网站的视觉气质。另外Vue3项目用Vite启动后热更新非常快改一行代码浏览器几乎秒刷新开发体验比旧工具链好太多。需要注意Vite需要Node14.18以上版本我用的是Node16运行很稳定。2.3 MyBatis-Plus简单查询靠内置方法复杂查询写XMLMyBatis-Plus最吸引人的就是BaseMapper接口提供selectById、selectList、selectPage、insert、updateById、deleteById这些现成方法整合后连基础Mapper XML都不用写。它的QueryWrapper条件构造器用起来类似链式调用比如查询上架中的景点并按照评分倒序代码就是new QueryWrapper ().eq(status, 1).orderByDesc(rating)。这比手写XML拼接SQL安全更重要的是在代码里能看到清晰的查询条件排查问题时思路直观。但MyBatis-Plus不是万能的遇到多表关联查询还是要回到XML里写SQL。比如查询“包含某景点且价格在某个区间的旅游线路”纯靠Wrapper做不出来正确的做法是在Mapper接口上定义方法然后去resources/mapper/xxxMapper.xml写自定义SQL。这里有一个很重要的实操细节MyBatis-Plus分页需要额外引入PaginationInnerInterceptor插件如果不配置查出来的数据会多出一页一页的全量数据而无视Page对象的number/size属性。这个坑我见太多人踩了后面排查章节专门说。2.4 MySQL8.0选它的理由不只是版本新很多旧教程用MySQL5.7但新项目我建议别犹豫直接在MySQL8.0上做。第一8.0默认字符集是utf8mb4存emoji和一些生僻字不会出现乱码这对旅游攻略这类含大量用户生成内容的系统尤其重要。第二8.0提供了窗口函数和公用表表达式CTE如果要统计每月订单数、排名靠前的热门景点这类报表数据SQL写起来非常优雅。第三8.0的JSON类型直接在数据库层支持如果以后要扩展景点动态属性字段不需要改表结构。但MySQL8.0有几个“到手先处理”的点。一是驱动类名从com.mysql.jdbc.Driver变成了com.mysql.cj.jdbc.Driver不换的话直接报错二是连接串必须带serverTimezone参数比如serverTimezoneAsia/Shanghai否则时间上有8小时时差三是8.0默认使用caching_sha2_password认证插件老版本的数据库连接工具连不上需要在建用户时指定mysql_native_password这个在部署阶段要注意。另外我建议建完库后把SQL脚本导出一份这份脚本就是论文里的数据库设计附录一举两得。组件本项目承担的核心职责常见替代方案选择理由SpringBoot2后端API基础框架SpringBoot3/SSH生态成熟、资料丰富、兼容性好Vue3前端页面交互Vue2/React组合式API结构清晰、Vite构建快MyBatis-Plus数据持久层MyBatis/JPA单表免写SQL、分页方便MySQL8.0数据存储5.7/PostgreSQL编码完善、窗口函数好用3. 核心功能与数据库设计3.1 建表思路一张表对应一个业务对象旅游网站的业务对象比较清晰我按“基础信息类、交易类、用户类、内容类”四个维度设计表。基础信息类包括景点表、旅游线路表、酒店表交易类就是订单表用户类包含用户表和角色表内容类对应攻略文章表。建议删掉“管理员单独建表”这种常见坏设计管理员本质是拥有管理角色权限的用户用角色字段区分即可一张用户表解决所有身份问题。以景点表为例字段上我设计了id、name、cover_image、imagesJSON类型存轮播图列表、category分类、address、description、price门票价格、rating、status、create_time、update_time这几个字段。注意两点第一图片不要直接存base64或者上传后的物理路径应该存相对路径如/upload/spot/xxx.jpg前端拼上后端域名即可访问这样将来换OSS或迁移服务器都灵活第二排序字段recommend_flag是否推荐单独落成一个字段首页推荐位直接查recommend_flag1的数据。3.2 订单表设计状态机是订单系统的核心订单相关字段要覆盖订单号、下单用户ID、线路ID或酒店ID、数量、总金额、状态、联系人信息等。这里的关键是“订单状态”字段我用的是int类型加注释的方式0代表待支付、1代表已支付待出行、2代表已经出行/已完成、3代表已取消、4代表已退款后端用常量类定义这些状态码业务逻辑里只有明确的状态流转开关位置才能改变状态值。这个设计比直接用字符串存“未支付”“进行中”这种状态要规范得多。订单号我用的不是数据库自增ID而是通过时间戳加随机数生成格式类似20250514132015001。这主要是为了避免订单号被遍历猜出体量也给后续对接支付接口留好了唯一业务单号的位置。另外一定要加创建时间create_time和更新时间update_time两个字段很多报表分析和排序都依赖它们别偷懒省掉。3.3 核心表结构SQL参考下面是景点表和订单表的建表SQL直接放到MySQL里执行即可。我在字段注释上写得比较详细这部分建议也保留下来写论文时可以直接用CREATE TABLE spot ( id bigint NOT NULL AUTO_INCREMENT COMMENT 景点ID, name varchar(100) NOT NULL COMMENT 景点名称, category varchar(50) DEFAULT NULL COMMENT 景点分类, cover_image varchar(255) DEFAULT NULL COMMENT 封面图, images json DEFAULT NULL COMMENT 轮播图列表, address varchar(255) DEFAULT NULL COMMENT 景点地址, description text COMMENT 景点介绍, price decimal(10,2) DEFAULT 0.00 COMMENT 门票价格, rating decimal(3,2) DEFAULT 5.00 COMMENT 评分, status tinyint DEFAULT 1 COMMENT 状态0下架 1上架, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT景点表; CREATE TABLE orders ( id bigint NOT NULL AUTO_INCREMENT COMMENT 订单ID, order_no varchar(32) NOT NULL COMMENT 订单编号, user_id bigint NOT NULL COMMENT 用户ID, product_type tinyint NOT NULL COMMENT 商品类型1景点门票 2旅游线路 3酒店, product_id bigint NOT NULL COMMENT 商品ID, product_name varchar(100) NOT NULL COMMENT 商品名称, quantity int NOT NULL DEFAULT 1 COMMENT 数量, total_price decimal(10,2) NOT NULL COMMENT 总金额, status tinyint NOT NULL DEFAULT 0 COMMENT 订单状态0待支付 1已支付 2已完成 3已取消 4已退款, contact_name varchar(50) DEFAULT NULL COMMENT 联系人, contact_phone varchar(20) DEFAULT NULL COMMENT 联系电话, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_order_no (order_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单表;3.4 表之间的关系与业务闭环用户和订单是典型的一对多关系一个用户能产生多条订单。旅游线路和景点是多对多关系一条线路由多个景点组成一个景点也会出现在不同线路中所以需要一张线路-景点关联表route_spot_rel。这种设计本身并不复杂关键是做业务闭环时要注意用户下订单后后台订单列表里要能查询到订单对应的用户昵称、商品名称等信息所以订单表里冗余了product_name和user_id关联查询虽然违反了“严格范式”但减少了联表次数查询性能更好。这是典型的空间换时间思路在面试时如果被问到为什么冗余字段答出这个逻辑是加分的。在此基础上攻略文章表和用户表关联记录作者ID收藏表关联用户和景点/线路记录收藏类型和目标ID。整个系统的数据关系就清晰了模块边界也自然划分出来旅游产品模块管景点/线路/酒店交易模块管订单内容模块管攻略用户模块管用户和角色。开发时按模块逐个完成不至于东写一块西写一块。4. 项目搭建与实操全过程4.1 环境准备与版本锁定动手之前先把环境捋清楚我建议这套版本组合JDK1.8或JDK11SpringBoot2都支持、Maven3.6、Node16、Vite4、MySQL8.0。JDK不建议上17除非你确定所有依赖都兼容。鼠标点一下开始菜单就能装好的东西不多讲了重点说说MySQL8.0的安装这里踩坑率极高Windows用户我推荐用ZIP压缩包方式安装解压后配置my.ini文件然后以管理员身份打开命令行执行mysqld --initialize-insecure初始化再执行net start mysql启动服务注意执行初始化命令时一定要用--initialize-insecure否则初始密码是随机生成的找起来非常麻烦。数据库装好后建议用Navicat或DBeaver连接。MySQL8.0默认的认证插件是caching_sha2_password老版本图形工具可能报“Authentication plugin”错误解决办法是在MySQL命令行里执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码;然后FLUSH PRIVILEGES;。这个操作我几乎在每个用MySQL8.0的项目里都要做一次顺手记下来不亏。4.2 后端工程结构与配置细节创建SpringBoot项目我推荐直接用IDEA的Spring InitializrGroup填com.kashgarArtifact填travel包名建议用com.kashgar.travel。工程结构按controller、service、mapper、entity、config、common、dto这样分包。Controller只做参数接收和结果返回业务逻辑全部下沉到Service层这个习惯从一开始就要养成。application.yml是整个后端配置的核心我贴一个能直接用的版本server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/travel_db?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/Shanghai username: root password: 123456 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8 servlet: multipart: max-file-size: 10MB mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml configuration: 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三个细节说一下。第一characterEncodingutf8mb4不是可选项缺了中文会乱码数据库表也要统一utf8mb4。第二jackson的date-format和time-zone决定了后端返回给前端的日期字符串格式不配的话前端拿到一串时间戳数字还要再做转换浪费不必要的联调时间。第三mybatis-plus的mapper-locations要配到classpath*:mapper/**/*.xml如果漏配置自定义SQL会报bound statement not found。4.3 前端工程搭建与页面结构前端用Vite创建命令是npm create vitelatest travel-web -- --template vue。装完基础依赖后再装vue-router、pinia、axios、element-plus这四个库是标配。目录结构上src/views按业务划分页面src/api按后端接口分类统一管理请求src/router配置前端路由。每个页面组件内部再拆子组件比如景点卡片SpotCard在首页、列表页都会被复用提炼出来可以明显减少重复代码。前端路由和权限是联动的。根据角色字段在路由守卫里判断游客只能访问公开页面带admin路径的管理页面必须在localStorage里有token且角色是管理员才能进入。这块代码写在router.beforeEach里每次路由跳转前检查一次。登录态的token我存在localStorage里因为刷新页面不能丢用户信息放在Pinia里因为它是全局共享的响应式数据。注意不要用SessionStorage存token否则用户关掉浏览器再回来就掉线了。4.4 跨域配置与前后端联调前后端分离后开发阶段必然遇到跨域问题。最简单的处理办法是在前端Vite的vite.config.js里配代理把/api开头的请求转发到8080端口这样浏览器看到的所有请求都是同源的就不会触发跨域拦截// vite.config.js export default defineConfig({ server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })后端同样配置跨域毕竟部署后前端可能跑在80端口、后端跑在8080端口这时候前端代理就帮不上忙了得后端放行。实现方式是实现WebMvcConfigurer接口并添加CorsRegistry映射允许的路径是/**允许来源是前端部署地址允许方法为GET、POST、PUT、DELETE。联调期间可以把allowedOriginPatterns设为*上线前再收紧。这个配置我在每个SpringBoot项目里都会写建议直接沉淀成配置类模板。后端有一个拦截器也是必须的。除了登录和注册接口以外所有接口都校验请求头里的Authorization字段是否存在。校验通过后从JWT里解析出用户ID放到ThreadLocal或RequestContext里供Service层使用。这段鉴权逻辑不要散落在各个Controller里重复写用一个HandlerInterceptor统一处理代码美观度会提升一个档次。4.5 核心接口设计与返回结构接口设计遵循RESTful风格资源名用复数。景点模块的接口有GET /api/spots分页查询景点支持keyword、category参数、GET /api/spots/{id}景点详情、POST /api/spots新增景点管理员、PUT /api/spots修改景点、DELETE /api/spots/{id}删除景点。订单模块有POST /api/orders创建订单、GET /api/orders/my当前用户订单列表、GET /api/orders/admin管理端订单列表、PUT /api/orders/status修改订单状态。统一返回结构用泛型类封装Data public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(success); result.setData(data); return result; } public static T ResultT error(String message) { ResultT result new Result(); result.setCode(500); result.setMessage(message); return result; } }这样一个类的收益前端响应拦截器里一处写死即可处理所有业务成功/失败场景。代码路径统一后任何接口返回结构都是同一套不会再出现“这个接口返回data、那个接口返回list”的混乱。4.6 部署从本地到服务器的完整路径项目开发完打包部署也是重要的一步。后端打包用Maven的package命令生成travel-0.0.1-SNAPSHOT.jar前端build生成dist目录的静态文件。如果你有一台云服务器最省钱的部署方案是在服务器上安装Nginx和JDKdist目录交给Nginx托管jar包用java -jar命令跑起来。Nginx配置一个location把/api开头的请求反向代理到本地的8080端口其余请求直接访问静态文件。配好后访问服务器的公网IP就能看到页面。linux环境下部署时有一个高概率踩坑的点MySQL8.0刚装完默认只监听localhost前端页面部署在Nginx后后端代码在服务器上直连localhost是没问题的但如果是本地连接服务器上的MySQL就要确认bind-address和用户权限是否允许远程访问。修改my.cnf的bind-address为0.0.0.0并给用户授权root%记得改完重启MySQL服务。安全组和防火墙也要放行3306端口但我不建议长期开着远程数据库端口用完及时关掉。5. 常见问题与排查技巧实录5.1 MyBatis-Plus分页失效这个算是老生常谈但永远有人踩。不配置PaginationInnerInterceptor的情况下Page对象传进来的current和size是无效的查询结果直接全量返回或者直接分页失效。解决方法是在项目里创建一个MybatisPlusConfig配置类注册MybatisPlusInterceptor Bean并添加PaginationInnerInterceptor(DbType.MYSQL)。然后Mapper方法接收Page参数例如Page selectPage(Page page, Param(ew) Wrapper wrapper)。如果想要自定义SQL分页在XML里写SQL时还要在Mapper接口里把Page作为第一个参数传入。这个配置是在项目里的一个大坑很多人报错之后在群里问“为什么我的selectPage不生效”一看就是没注册插件。5.2 前端请求404/无法访问后端接口排查思路永远是从外到内逐层看。第一步看浏览器Network面板请求有没有发出、状态码是多少第二步看后端控制台日志有没有对应请求的访问记录第三步看代理配置和后端上下文路径是否匹配。最常见的原因是baseURL和代理路径不一致比如前端axios的baseURL是/api但vite代理里写的是/api而后端Controller的RequestMapping没带/api前缀结果请求被代理转发到了后端一个不存在的路径上。统一约定前端请求路径带/api前缀后端所有Controller的RequestMapping统一以/api开头这样代理转发和目标映射就能对上了。5.3 JWT过期后前端报401但页面无跳转这个问题的根源在于响应拦截器没有处理401状态码。正确的做法是在axios响应拦截器里当response.data.code等于401或者HTTP状态码等于401时清除本地token、清空用户信息并跳转回登录页。注意跳转时要用window.location.href或router.push但拦截器文件里拿不到router实例最简单的办法是直接用window.location.href /login。同时给后端一个约定token过期或缺失时统一返回401前端只看这个状态码就做统一处理不要每个接口单独写判断。5.4 部署后图片不显示或样式丢失Nginx托管前端静态资源时默认只认html文件和静态资源文件。图片上传功能在后端实现时通常会存到一个本地目录比如/upload这样需要为这个目录单独配一条Nginx的location规则让它能把/upload路径映射到服务器上实际的文件目录。配置如下server { listen 80; server_name your_domain; root /www/travel-web/dist; index index.html; location /api { proxy_pass http://localhost:8080; } location /upload { alias /www/travel-web/upload/; } location / { try_files $uri $uri/ /index.html; } }这里把/api反向代理给后端 /upload直接指向上传文件的物理位置 /用try_files回退到index.html这样Vue Router使用history模式时刷新页面不会404。上传文件时的目录权限也要留意服务器上需要确保运行用户对上传目录有写权限否则报“No such file or directory”让人摸不着头脑。5.5 时间差了8小时 / 数据库连接串认证报错这个几乎每个项目都会被问到。MySQL连接串一定要带serverTimezoneAsia/Shanghai后端Jackson也要配GMT8数据库表字段用datetime类型而不是timestamp。三处都配好前后端展示的时间才不会差8个小时。另外如果是Linux服务器上装的MySQL8.0默认时区是system可以先执行SELECT NOW();看看服务器当前时间对不对不对的话在my.cnf里加一行default-time_zone08:00重新加载驱动层面就不会再做时区换算。数据库连接串的caching_sha2_password报错本质是MySQL8.0默认认证插件和旧的客户端驱动不兼容。如果你还在用比较老的mysql-connector-java版本建议升级到8.x或者按前面说的把用户改成mysql_native_password认证。这里我给一个建议直接用mysql-connector-j:8.0.33驱动类名写com.mysql.cj.jdbc.Driver这个组合在SpringBoot2.7和MySQL8.0之间是最稳的。问题现象常见原因解决动作分页数据总量不对未注册分页插件配置PaginationInnerInterceptor中文乱码连接串缺utf8mb4连接串加characterEncodingutf8mb4接口返回401但页面无反应拦截器未处理401响应拦截器跳转登录页请求找不到后端路径baseURL和代理路径不一致统一约定/api前缀图片刷新后丢失Nginx未映射上传目录配置/upload的location规则时间相差8小时时区参数未统一连接串、Jackson、数据库时区三处统一表别名导致字段映射失败MyBatis-Plus默认驼峰映射开启mapUnderscoreToCamelCase或起别名5.6 MyBatis-Plus字段映射的坑MyBatis-Plus默认开启驼峰映射也就是数据库字段user_name自动映射到实体类userName。但如果你在关联查询的SQL里给表起了别名比如select s.id, s.name as spotName from route_spot_rel r left join spot s on r.spot_id s.id where r.route_id #{routeId}这时候如果实体类里有spotName字段MyBatis-Plus是按resultType自动映射的底层还是靠下划线转驼峰的规则字段别名直接用驼峰命名反而会映射不上。稳妥做法是给别名写下划线形式比如s.name as spot_name实体类属性仍叫spotName。这个细节排查起来比较隐蔽我在给线路详情接口加景点列表时就遇到了浪费了半小时。另外一个字段映射的坑是Boolean类型字段的命名。Java实体类里如果把字段命名为deleted生成的getter方法是getDeleted反过来也正常。但如果命名为isHotLombok生成的getter方法会变成getIsHot()而MyBatis-Plus在解析时会把它当成字段hot结果就是断言字段不存在或映射失败。实体类里的布尔字段建议一律不用is前缀直接用hot这种命名问题就没了。5.7 Vue3开发中的组合式API细节Vue3开发这块我分享几个高频问题。第一setup里用ref定义数组时操作要赋值给refValue.value例如spots.value resp.data忘记写.value是新手常见错误。第二onMounted里发请求时组件可能被用户快速切走异步返回后去操作已被卸载组件的DOM会报错可以在请求前设置一个isUnmounted标志或者在异步回调判断一下组件是否仍然存活这个在移动端列表页尤其常见。第三Vue3组件使用v-model时默认走modelValue/update:modelValue和Vue2的value/input不一样封装自定义表单组件时容易写错。第四Element Plus的Form表单校验规则在Vue3下要在script setup里用reactive定义并且el-form-item的prop要和规则里的字段名一一对应否则点击提交时校验永远不触发。这几个问题我几乎每做一个Vue3项目都会被问到建议直接记下来。6. 项目文档与毕业设计的衔接6.1 开发文档里写什么项目标题里带了“含文档”这个文档的价值在很多同学眼里被低估了。我建议文档按这个结构组织需求分析、系统设计架构图功能模块图、数据库设计ER图表结构说明、核心代码讲解、系统测试与运行截图、部署说明。其中数据库设计部分把建表SQL、表字段说明、索引设计放进去核心代码讲清楚JWT鉴权、分页查询、订单状态流转这三个点这三段就是答辩时最能体现工作量和技术深度的内容。运行截图建议每页保存功能模块的代表性页面统一尺寸和命名比如01-首页.png、02-景点列表页.png不要随便截了一大堆然后全部堆在文档末尾而是应该分布到对应功能章节里做到图文穿插。设计文档用Word写没问题但画架构图推荐用draw.io产出SVG排版清晰又方便修改导出的图插到文档里不会糊。6.2 答辩时怎么讲这个项目答辩的核心是讲清楚业务逻辑和技术实现抓两个重点就够了第一个重点是前后端分离的请求链路从用户点击页面到后端返回数据中间涉及跨域、JWT鉴权、Controller/Service/Mapper三层调用、数据库查询整条链路串下来讲三分钟这已经能体现系统设计能力第二个重点是某个具体模块的实现细节比如订单状态机或者线路分页查询讲清楚为什么这样设计字段、数据如何流转。不要讲太多框架概念老师更想听到的是你对项目本身的思考和细节把控。下面梳理一份建议的项目计划按每周为一个单位推进很适合学生党在4~8周内完成第1周选定题目安装环境初始化前后端工程完成数据库建库建表第2周后端实现用户注册登录和JWT鉴权前端实现登录注册页面并打通联调第3周实现景点、线路、酒店模块的增删改查和前端页面第4周实现攻略模块和首页推荐位逻辑第5周实现订单模块的状态流转前后端完成下单流程第6周完善后台管理页面调整样式修复边界情况第7周整理测试用例处理异常问题完善文档第8周部署上线录制演示视频准备答辩PPT这个计划是按每天投入2到3小时估算出来的按这个节奏做时间上不会太压抑完成度也高。7. 写在最后的实操经验最后分享一点个人经验。做这类系统前期的数据模型设计一定要认真。我在带学生做项目时发现凡是执行到中途开发效率骤降的项目九成都是因为数据库表没设计好字段语义不清晰、缺少关联索引、状态字段没有统一约束。后期改表结构的代价远大于前期多加半天思考时间的代价这个教训希望你们第一次做完项目就能体会到。再一个建议是保留好开发过程中的每一步记录。我当时把接口文档、SQL脚本、前端组件截图、联调遇到的问题全部记在项目目录下的docs文件夹里最后写文档时直接复用这些素材效率极高而且这些记录本身就是你实践经验的一部分。真到了毕业答辩或者面试聊项目时你能说出“当时做跨域处理时遇到什么问题、怎么排查的、最后怎么解决”这种真实经历比背一百个面试答题模板都有说服力。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门