SpringBoot+Vue+MySQL招生宣传管理系统开发实战解析
2026/9/10 5:50:45 网站建设 项目流程

1. 招生宣传管理系统到底在管理什么:业务边界和模块拆解

拿着完整源码第一件事别急着跑,先看清楚这个项目要解决什么问题。招生宣传管理系统,这个名字看着长,但拆开看就清楚了:招生、宣传、管理。招生的核心是获取咨询线索,宣传的核心是分发内容素材,管理的核心是把这两拨数据沉淀到系统里,让招生办不再靠Excel和微信群过日子。

我之前接触过不少高校的招生季现场,最典型的场景是这样的:招生简章改到第8版还是有人拿错、咨询电话打进来要现翻笔记本找之前的沟通记录、每天下班前把新加的微信好友姓名电话手动录入表格。这还只是信息层面,更麻烦的是数据统计——这个月电话咨询来多少条、线下宣讲会引流多少条、哪个渠道投放的转化率最高,全靠人工数,数完还不一定对。

这个系统就是冲着这些问题去的。它的业务边界可以划成四条线:内容发布线、物料管理线、咨询线索线、数据统计线。每条线对应若干功能模块,模块之间用状态流转串联起来,就构成了一个完整的业务闭环。

1.1 从办公桌前的实际场景反推系统功能

如果你去问招生办的老师“你最想要什么功能”,大概率得到的答案是“能方便点就行”。但作为开发人员,得把“方便”翻译成具体的功能点。

  • 资讯发布:招生简章、专业介绍、录取政策这类内容,需要有一个发布后台,支持富文本编辑、封面图上传、定时发布和下架。草稿、待审核、已发布、已下架这四种状态是必须的,因为招生政策经常调整,旧内容不能直接删,得有个可控的下线流程。

  • 材料库管理:横幅图、易拉宝源文件、短视频、H5链接,这些都是宣传物料。系统里应该有分类管理、上传预览、版本记录和下载权限控制。物料这东西最怕版本混乱,所以保留历史版本比“删除旧文件”更安全。

  • 咨询登记与跟进:这是整个系统的价值核心。每一个咨询来源(电话、官网表单、线下展会、渠道推广)都要留存,每个线索都有负责人、跟进状态(新建、已联系、已报名、已流失)和下次跟进时间。这一块做好了,招生办能直接从系统里导出数据做回访。

  • 统计看板:按时间维度看线索总量和转化率,按来源渠道看投放效果。不需要做复杂的数据可视化,基础的柱状图和折线图就能满足90%的汇报需求。

  • 系统管理:用户、角色、菜单、字典、操作日志。这就是后台管理系统的地基,每个正经项目都必须有,不然没法控制谁有权发布内容、谁能导出咨询数据。

1.2 功能模块地图:菜单背后是一条条业务流

把上面的需求落成菜单,大概是这张图的样子:

模块名核心功能对应的业务场景
招生资讯管理资讯增删改查、审核状态流转、封面图上传招生简章和政策的发布与下线
宣传材料库材料上传、分类归档、在线预览、版本记录宣传海报、视频、H5的集中管理
咨询线索管理线索录入、跟进状态流转、分配负责人各渠道咨询汇总和回访跟踪
数据统计看板线索量统计、来源分析、转化率报表招生效果评估和例会汇报
用户与权限用户管理、角色分配、菜单权限控制管理员、招生专员、内容编辑的权限隔离
系统配置字典管理、操作日志、基础参数配置维护系统的可配置性而非硬编码

这里有个容易被忽视的设计点:状态流转。比如资讯的状态从“草稿”到“已发布”,咨询线索从“新建”到“已报名”,这些流转最好由字段值控制,而不是直接改状态字符串。用字典表维护状态的含义,后面前端下拉框和后端枚举都从字典取值,改起来不用动代码。很多初学者直接写死字符串,后面想加一个“待回访”状态就得满项目找if判断,非常痛苦。


2. 为什么是SpringBoot+Vue+MySQL+MyBatis这一套组合

技术选型这件事,看起来像“跟风”,其实背后都有现实理由。如果你去搜“springboot框架介绍”,你会发现它解决的是Java后端开发里配置繁琐、部署笨重的问题。而“vue安装及环境配置”能成为热门词,说明Vue在前端工程化里的地位已经成了默认选择。两者加在一起,配合MySQL和MyBatis,就构成了一条极其成熟的前后端分离开发链路。

2.1 每个组件都是冲着管理系统的痛点去的

先说SpringBoot。管理类系统的特点是接口多、逻辑重复度高、CRUD占大头。用传统SSH框架写,光XML配置就能占掉一上午;SpringBoot用自动配置和starter机制把这些默认值全部藏起来,开发人员只需要关注业务代码。内嵌Tomcat这一点对部署也是降维打击——一个jar包一把跑起来,不用再装外置容器。对做毕设或者个人项目的人来说,这意味着把精力花在业务实现而不是环境搭建上。

再来说MyBatis。管理系统里最频繁的操作是列表查询和统计报表,这类SQL往往是多表关联、条件动态拼装的。MyBatis这类半自动ORM的优势就在这:SQL写在自己手里,想怎么优化就怎么优化。JPA/Hibernate虽然全自动,但复杂查询时生成什么SQL你控制不了,调优时就很被动。MyBatis还支持动态SQL,<if>标签就能搞定多条件筛选,这点对管理系统的搜索功能来说是大杀器。如果觉得原生MyBatis写XML太繁琐,可以换MyBatis Plus,但要注意Plus在批量插入、逻辑删除上确实省事,可它的自动填充和乐观锁实现,底层原理还是要懂原生MyBatis才能用好。

前端选Vue的理由更直接。后台管理页面无非是表格、表单、弹窗、选项卡,这是Vue的舒适区。双向绑定让表单处理少写一堆getElementById,组件化开发让表格和弹窗可以被多次复用,Vue Router负责菜单导航,Vuex/Pinia管理登录状态。整个前端项目的代码量,用原生JS写大概是Vue的三倍以上。至于React当然也能做,但Vue的中文文档和社区活跃度,对新手更友好,遇到问题容易搜到答案。

2.2 SpringBoot版本陷阱:选错版本会让人崩溃

热词里有“springboot版本太高”这个搜索词,我敢说搜这个词的人,十有八九是遇到了SpringBoot 3.x和旧版依赖不兼容的问题。SpringBoot 3.x强制要求JDK17+,而很多教材和考试环境还是JDK8。如果你本机是JDK8,却拉了一个SpringBoot 3.2的项目,启动时会直接报UnsupportedClassVersionError,这个错看着很吓人,其实就是版本不匹配。

我的建议是:如果做毕业设计或者课程项目,优先选SpringBoot 2.7.x版本,这是2.x的最后一个稳定版本,兼容JDK8,也兼容大部分主流starter。如果是新学,直接看官方文档用3.x也行,但要确保JDK版本跟得上。

具体到Maven依赖,核心parent节点这样写:

<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> <relativePath/> </parent>

这样SpringBoot自动帮你锁定了一大堆依赖版本,不用自己去担心版本冲突。如果你非要自己指定某个依赖的最新版,一定要看清楚它是否兼容当前SpringBoot版本,比如Druid连接池、PageHelper分页插件,都有对应的版本适配要求。


3. 数据库设计:招生场景下的表结构和关键字段

这个项目最值得细看的就是数据库。表设计不好,后面写代码就是灾难。管理系统的表通常分三类:系统权限类、业务数据类、配置字典类。这里我重点讲业务表的几个关键设计点,因为系统权限类的用户角色表所有项目都大同小异,查一下Spring Security或者Shiro的示例就能照搬。

3.1 核心业务表字段拆解

以咨询记录表为例,它是整个招生系统数据流的终端,几乎每个模块都在给这张表喂数据:

CREATE TABLE `consult_record` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL COMMENT '咨询人姓名', `phone` varchar(20) NOT NULL COMMENT '联系电话', `source_type` varchar(30) DEFAULT 'PHONE' COMMENT '来源渠道: PHONE/WEB/EXHIBITION/CHANNEL', `status` tinyint(4) DEFAULT 0 COMMENT '跟进状态: 0新建 1已联系 2已报名 3已流失', `assign_to` bigint(20) DEFAULT NULL COMMENT '负责人ID', `remark` varchar(500) DEFAULT NULL COMMENT '备注', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `is_deleted` tinyint(1) DEFAULT 0 COMMENT '软删除标记', PRIMARY KEY (`id`), KEY `idx_phone` (`phone`), KEY `idx_source_type` (`source_type`), KEY `idx_create_time` (`create_time`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

这张表里有一个非常关键的业务设计:source_type字段。为什么咨询记录必须记录来源?因为招生推广要评估渠道ROI——线下展会发出去的海报带来了多少扫码,微信公众号推文带来了多少电话,搜索引擎投放带来了多少表单。没有这个字段,统计看板就无从谈起。这属于典型的“业务驱动设计”,不是拍脑袋加字段。

3.2 软删除、时间字段、逻辑外键:这些约定你必须懂

先看软删除。is_deleted这个字段几乎每个业务表都有,为什么不用物理DELETE?两个原因:一是数据需要追溯,比如一个咨询线索被误删了,如果物理删除就真的找不回来了,软删除还能翻;二是关联数据的存在性校验——资讯下面可能关联了浏览记录、收藏记录,物理删除会让关联数据变成孤儿数据。所以约定俗成:业务表一律软删除,查询条件里统一加WHERE is_deleted = 0,这可以用MyBatis的XML里统一写SQL片段来减少重复。

再看时间字段。create_timeupdate_time是标准配置。MySQL 5.7以上支持DEFAULT CURRENT_TIMESTAMP,但如果你用MyBatis,也可以在插入和更新时通过Java代码设置。我建议数据库层和代码层都设置,双保险。

还有一点容易被问到的:物理外键到底用不用?这是老生常谈的话题。我个人的实践是:管理系统里不用数据库物理外键约束,把外键关系留在应用层去校验。原因是物理外键在插入数据时会有额外的约束检查,影响性能;而且很多团队后续要做分表分库或者迁移,物理外键会成为巨大的阻碍。但表设计上逻辑外键关系要清楚,比如assign_to指向用户表的id,联表查询时用JOIN查出来,这就可以了。

3.3 索引设计的思路

管理系统的查询模式很固定:按条件筛选列表、按主键查详情、按时间范围做统计。针对这个特点,索引设计遵循几个原则:

  • 查询频率高的等值字段建索引。phone就是要高频查询的字段,咨询回访时第一件事就是搜手机号。
  • 统计报表常用的时间字段建索引。create_time加索引后,按天/月分组的统计会快很多。
  • 联合索引要结合具体查询场景。比如同时按source_typestatus筛选时,建一个(source_type, status)的联合索引比两个单列索引更高效。
  • 避免冗余索引。很多新人看到字段就加索引,这会导致插入更新变慢,还占用磁盘空间。

有一个常见的坑是字段类型选错。比如手机号如果用int类型存储,号码超过int最大长度会溢出,这就是热词里“mysql中int+5”这类问题的根源。手机号、身份证号这类固定长度的数字字符串,必须用varchar,这一点在表设计时就要定好,不然后面改字段类型代价极大。


4. 后端实现细节:SpringBoot和MyBatis联动中的关键点

后端代码的骨架是Controller-Service-Mapper三层架构,这个没什么好说的。但有一些细节不仔细看会踩坑,这里按实际开发顺序讲一下。

4.1 配置文件里最容易踩的坑

application.yml是后端启动的第一道关卡。管理系统的数据源配置和MyBatis配置如下:

spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/recruit_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true username: root password: 123456 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.recruit.entity configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

这里有几个特别容易出问题的点。

第一个是mapper-locations。如果你把Mapper接口写在com.example.recruit.mapper包下,XML文件放在resources/mapper/目录,这两边对应关系必须一致,否则启动时会报Invalid bound statement (not found)。初学者最常见的卡壳就在这,明明接口写了方法,XML也写了SQL,怎么还是找不到?多半就是路径不对或者XML文件名和接口名不完全一致。

第二个是map-underscore-to-camel-case。数据库字段是create_time,Java属性是createTime,这个配置打开后MyBatis会自动映射。忘了配置的话,查询结果里createTime永远是null,而且很难排查。

第三个是StdOutImpl日志。热词里有人搜“mybatis配置打印”,说明很多人遇到过SQL不显示不知道对错的问题。开发阶段把日志输出到控制台,可以直观看到每次调用的SQL和参数,排错效率翻倍。生产环境再关掉或者改成别的实现。

4.2 #和$的区别,以及为什么必须用#

这个点是Java面试里的高频题,也是实际开发中的安全红线。#{}是预编译占位符,MyBatis会把它转成?,再用PreparedStatement设置参数,天然防SQL注入;${}是字符串拼接,直接把值嵌进SQL语句里,拼接进去了就等于执行了用户输入的东西。

举个实际例子,登录查询如果写成:

SELECT * FROM sys_user WHERE username = '${username}' AND password = '${password}'

用户在用户名框输入admin' --,整个SQL就变成了:

SELECT * FROM sys_user WHERE username = 'admin' -- ' AND password = 'xxx'

--后面全部变成注释,这就等于绕过了密码校验。这个场景我说过很多次了,但每次搜“mybatis中的#和&的区别”还能看到这个话题热度,说明踩坑的人是真多。

什么时候必须用${}?动态排序字段、动态表名。这两种场景下#{}会带着引号导致SQL语法错误。但用${}时必须做白名单校验,比如排序字段限定为ascdesc,字段名限定为Java代码里预设的几个值,绝对不能让用户随便拼。

4.3 统一返回结构和全局异常处理

写Controller时如果每个接口返回格式都不一样,前端联调会极其痛苦。所以第一个约定就是统一返回结构:

@Data public class Result<T> { private Integer code; private String message; private T data; }

成功返回code=200,业务失败返回code=500或自定义错误码,前端拿到code不等于200就走错误提示。这个约定简单、实用,能省掉一堆联调阶段的扯皮。

全局异常处理用@RestControllerAdvice,把业务异常、参数校验异常、兜底异常分开处理。比如参数校验失败返回提示信息,用户友好一些;未知异常统一返回“系统繁忙”,不要把堆栈信息直接甩给前端。

4.4 分页和批量插入的实战

列表查询必须分页,这是管理系统的刚需。用PageHelper分页插件是最省事的方案,用法很简单:

PageHelper.startPage(pageNum, pageSize); List<ConsultRecord> list = consultRecordMapper.selectByCondition(condition); PageInfo<ConsultRecord> pageInfo = new PageInfo<>(list);

注意PageHelper是线程局部变量,调用startPage后紧接着执行的第一个Mapper查询才会被拦截分页。中间稍微绕一下(比如先查了别的数据),分页就会失效,这是一个隐藏很深的坑。

批量插入用MyBatis的foreach写法,例如咨询线索的Excel批量导入,一次可能要插入几百条。SQL大概长这样:

<insert id="batchInsert" parameterType="list"> INSERT INTO consult_record (name, phone, source_type, status, create_time, is_deleted) VALUES <foreach collection="list" item="item" separator=","> (#{item.name}, #{item.phone}, #{item.sourceType}, #{item.status}, NOW(), 0) </foreach> </insert>

批量插入虽然一次能插很多,但MySQL的max_allowed_packet有上限,如果单次插入上千条,可能会报packet太大的错误。稳妥的做法是分批插入,比如每500条一次。另外,MyBatis一级缓存是SqlSession级别的,批量插入后如果还要查这些数据,别忘了提交事务,否则事务隔离级别下自己都查不到,这是并发控制里的经典场景。


5. 前端Vue部分:管理后台的开发和联调要点

5.1 环境准备:先把vue-cli和项目骨架搭起来

Vue后台管理系统的开发,先得把环境搞定。node.js、npm、vue-cli这三件套是基础。如果你还没有装,直接用命令:

npm install -g @vue/cli vue create recruit-admin

项目创建时,建议选择“Manually select features”,勾选Router、Vuex、ESLint。Element UI或者Element Plus如果是用Vue开发管理系统的,默认就该装一个,表格、表单、弹窗这些组件开箱即用。安装指令:

npm install element-plus --save

前端最让人头疼的不是写代码,而是环境起不来。常见问题包括:node版本过旧导致vite或webpack报错、npm镜像拉包慢导致超时、依赖版本和项目预期不符。遇到这些问题,先换淘宝镜像源:

npm config set registry https://registry.npmmirror.com

装完依赖,执行npm run serve之后,访问localhost:8080能出页面,环境就通了。

5.2 路由守卫和登录态管理

管理后台的页面结构大概是登录页、布局页(带侧边栏)、各个功能页。登录后要跳转到首页,未登录访问任何页面都要被拦回去,这就要用Vue Router的全局前置守卫:

router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') if (to.path === '/login') { next() } else { if (!token) { next('/login') } else { next() } } })

这个守卫逻辑看起来简单,但90%的后台系统都是这么写的。更复杂的动态路由(根据用户权限生成菜单)一般出现在中大型系统里,如果只是做毕设,优先级不高。把静态路由写清楚、页面组件按模块拆分好,就已经达到入门水平了。关于token的存储位置,localStorage是常见做法,但要注意XSS风险;也可以用cookie加httpOnly属性,配合后端的session方案。管理系统的安全级别本身就是“够用就好”。

Axios拦截器是前后端联调的关键。请求拦截器统一在Header里加token,响应拦截器统一处理业务错误码和401状态码:

axios.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers['Authorization'] = token } return config }) axios.interceptors.response.use( response => { const res = response.data if (res.code !== 200) { ElMessage.error(res.message) return Promise.reject(new Error(res.message)) } return res }, error => { if (error.response && error.response.status === 401) { router.push('/login') } return Promise.reject(error) } )

写到这里,前后端的接口约定就自然形成了:后端返回{code, message, data},前端拦截器统一解包,业务逻辑里拿到data直接用。这套模式跑顺了,联调阶段几乎不会卡壳。

5.3 跨域问题:后端处理才是正解

前后端分离项目最经典的问题就是跨域。开发时前端跑在8080端口,后端跑在8081端口,默认情况下浏览器的同源策略会拒绝后端的响应。解决跨域有很多方案,比如前端用Vue的devServer代理,但更推荐在后端统一配置CORS,这样后续部署到生产环境时,前后端域名不一致也不会出问题:

@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }

allowedOriginPatterns("*")配合allowCredentials(true),可以允许所有来源并携带凭证请求。这里有个细节:如果前端用的请求头不是默认的Content-Type,OPTIONS预检请求可能被后端拦截,所以allowedMethods里必须加上OPTIONS

5.4 招生宣传视频播放:Vue里遇到m3u8别慌

招生宣传系统里经常要放宣传视频,这块如果在后端用OSS存文件、前端用video标签播mp4,一切都很正常。但现实是很多学校或机构的视频文件是流媒体格式,尤其是m3u8。为什么要用m3u8?它支持HTTP直播流,切成小分片可以边下边播,支持自适应码率,手机端和弱网环境下体验比直接拉mp4好得多。缺点是如果直接拿video标签播m3u8,浏览器原生不支持。

在Vue里处理m3u8,主流方案是用hls.js

import Hls from 'hls.js' function initPlayer(videoEl, url) { if (Hls.isSupported()) { const hls = new Hls() hls.loadSource(url) hls.attachMedia(videoEl) } else if (videoEl.canPlayType('application/vnd.apple.mpegurl')) { videoEl.src = url // Safari原生支持m3u8,直接赋值 } }

这个方案兼容性非常好:iOS的Safari走原生分支,其他浏览器走hls.js。唯一要提醒的是,m3u8文件本身是文本文件,里面指向的ts分片地址如果是相对路径,那后端返回视频时必须处理好路径拼接,不然会一直请求不到资源。另外跨域CORS对m3u8的播放也同样生效,播放器的请求域名也要加入后端CORS白名单。


6. 从源码到跑通:环境配置全流程和我的踩坑记录

很多人拿到的源码本身是完整的,但就是跑不起来。每次看到有人同时搜“springboot项目”、“mysql安装配置教程”、“vue路由”、“vue devtools插件下载”,我就知道这又是一个在环境上卡住的朋友。这里把我实际跑过这类项目之后的完整流程和几个典型问题写一遍。

6.1 一步不落的启动顺序

第一步,安装JDK。如果你用的SpringBoot是2.x,装JDK8就行;如果是3.x,必须JDK17+。对着项目的pom.xml看,如果java.version是1.8,那就别犹豫,装JDK8。

第二步,安装MySQL。官网下载MySQL Community Server,Windows环境一直下一步就能装完。安装过程中会要求设置root密码,这个密码要记好,后面连接数据源要用。装完用Navicat或者MySQL Workbench连上,新建数据库,然后把项目里SQL文件导入。注意:SQL文件里如果包含CREATE DATABASE,你直接导入会报错,先在Navicat里建好同名的库,然后右键选择“运行SQL文件”。

第三步,启动后端。用IDEA打开项目后,先等Maven把依赖下载完。这里强烈建议把Maven的settings.xml里配好阿里云镜像,否则把依赖拉完可能需要半小时甚至更久。配置文件里的数据库密码改成你本机的,然后启动Application类,看到“Started Application in xxx seconds”就说明后端起来了。

第四步,启动前端。命令行工具进入前端目录,依次执行:

npm install npm run serve

等编译完成,浏览器访问localhost:8080,能出现登录页就说明环境全部就绪。在这里,默认账号密码通常在项目的README或者数据库的admin表里,一般就是admin/admin123之类的。

6.2 MySQL 8.0的常见报错:时区、字符集、驱动

MySQL 8.0和5.7的差异,在跑项目时集中爆发为三个报错。

第一个是The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized。这看起来像乱码,实际上是时区问题。解决方案是在数据库连接URL上加上serverTimezone=Asia/Shanghai,后面再补一个useSSL=false避免SSL握手警告。

第二个是Public Key Retrieval is not allowed。这个错误出现在MySQL 8.0的caching_sha2_password认证方式下,老版本的JDBC驱动不认识它的公钥获取流程。解决方案是在连接URL末尾加上allowPublicKeyRetrieval=true

第三个是中文乱码。建库时如果没有显式指定字符集,会默认用latin1,插入中文数据就变成???。数据库连接URL之前已经写了characterEncoding=utf8,这还不够,建库时要执行:

CREATE DATABASE recruit_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

utf8mb4是utf8的超集,能存emoji和生僻字,现在的项目创建库默认都应该用这个。

6.3 联调阶段最容易翻车的三个问题

  • 端口占用。后端默认8080端口,前端Vue开发服务器也是8080,两个同时启动必然有一个冲突。要么把后端端口改成8081,要么把前端端口改成8081。改后端在application.yml里改server.port,改前端可以在vue.config.js里配置devServer.port。我习惯后端固定8080,前端用8081。

  • 刷新页面404。前端路由如果用的是history模式,开发环境下刷新子页面会出现404。原因很简单:刷新时浏览器向后端服务器发起了实际请求,但后端并没有对应的路由处理,于是404。本地开发时可以用Vue的devServer配置historyApiFallback: true解决;生产环境部署时,要么后端加一个转发规则把非API请求转发到index.html,要么直接用hash模式。对于管理系统,hash模式的/#/虽然丑了点,但简单省事,不用麻烦运维改Nginx。

  • 登录成功但请求全部401。这种问题十有八九是token没有正确传到后端拦截器里。前端Axios请求拦截器里设置了Authorization头,但后端过滤器或拦截器的名称写成了别的,比如tokenHeader,对齐一下即可。另一种情况是前后端对token的“Bearer ”前缀约定不一致,前端加了后端没解,也会401。

6.4 我实际跑完这个项目的几点建议

第一,拿到源码后,先不要急着改代码,先按上面流程把项目原封不动跑起来。跑通之后再开始读代码,这样你已经有一个“正常参照物”,后续改动如果改坏了,可以对照差异排查。

第二,看日志时不要只看报错的那一行,要往上翻几行。SpringBoot的异常栈信息里前几行往往是“错误原因汇总”,真正有用的业务错误通常藏在中间,比如某个SQL字段不存在,可能先抛的是空指针,再往下翻才是MyBatis的SQL日志。

第三,如果你的后端启动时报错又排查不出来,先试试把MyBatis的日志打开,看最后一条执行的SQL是什么。很多时候问题不在代码逻辑,而是SQL和表结构对不上。

第四,尽量不要在下载依赖时手动改版本号。SpringBoot的starter全家桶内部有一套版本兼容矩阵,你手动把某个公共依赖升到新版,往往会把另一个依赖打破。真有升级需求,先查官方文档的依赖版本对照表。

最后再讲一个心得:像招生宣传管理系统这种项目,技术上没有多高深,但它赢在业务完整——从前端的页面交互,到后端的接口设计,再到数据库的字段约束,是一条完整链路。把这条链路上的每个节点都亲手跑一遍、改一遍、调一遍,你对SpringBoot的自动配置、MyBatis的SQL映射、Vue的组件通信、MySQL的表设计就会有一个整体认知,这种认知是看再多零散教程都换不来的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询