ARTICLE DETAIL

资讯详情

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

SpringBoot+Vue+MyBatis迎新系统开发实战:从环境配置到部署全流程

SpringBoot+Vue+MyBatis迎新系统开发实战:从环境配置到部署全流程 1. 项目整体设计与业务拆解每年七八月份大学迎新系统都是刚需中的刚需。新生还没报到信息录入、宿舍分配、报到进度统计、缴费核对这一堆活儿就得先跑起来。很多学校还在用Excel表来回传费时费力还容易出错。正因如此基于SpringBoot Vue MyBatis MySQL 的迎新管理系统成了Java全栈开发里特别有代表性的实战项目。这套技术栈在市场里有多常见不用我多说。SpringBoot负责后端接口MyBatis操作数据库MySQL存数据前端用Vue把页面和交互撑起来。要说为什么这么多人用这个组合原因很直接SpringBoot让项目配置变得特别轻不用再像Spring MVC时代那样写一堆XMLMyBatis半自动化的SQL控制让复杂查询、多表联查都掌握在开发者手里Vue的组件化开发在管理后台这种场景下效率极高MySQL则天生适配中小型项目的成本和性能需求。四者结合既不过度设计又能覆盖绝大多数业务场景。这个项目适合谁来参考如果你想学全栈开发或者想找一个贴近真实业务、能写到简历上的管理系统项目那这套迎新系统的思路和代码可以直接拿过去用。它不只是一个玩具项目里面涉及权限控制、数据导入导出、按条件分页查询、多表关联、前端路由守卫、Axios拦截这些真实开发中的高频需求把整套流程走完你对全栈开发的理解会上一个台阶。2. 环境准备与项目初始化2.1 版本搭配与安装建议动手之前先把环境版本理清楚。我见过太多项目跑不起来的案例最后排查下来都是版本搭配出了问题。后端这块JDK建议用8或11。JDK8虽然老但绝大多数公司线上还在用兼容性最好如果你的SpringBoot版本是2.7以上的那JDK8完全够用。SpringBoot的话2.5.x到2.7.x都可以没必要追求3.x——3.x对JDK版本有要求且部分第三方组件适配还没那么成熟做这个项目用2.7.5这种稳定版本最稳妥。Maven建议3.8以上Node.js建议14以上npm要跟得上。前端Vue版本是另一个容易踩坑的地方——Vue 2和Vue 3的语法、生态差别不小如果项目源码用的是Vue 2就别手痒升级到Vue 3不然Element UI、Vue Router的版本全得跟着变一个上午就交代给依赖兼容了。MySQL版本5.7或8.0都可以。如果你用的是8.0注意驱动要改成com.mysql.cj.jdbc.Driver同时URL里要加时区参数serverTimezoneAsia/Shanghai不然连数据库直接报时区错误。2.2 初始化SpringBoot后端工程后端工程建议直接用IDEA创建选Spring Initializr时勾上Web、MyBatis、MySQL Driver这几个依赖。如果是从零开始建不用太纠结自动生成的目录结构重要的是搞清楚每层代码放哪controller接收前端请求做参数校验调用service层service业务逻辑处理事务控制在这一层加mapperMyBatis的Mapper接口定义数据库操作方法entity实体类对应数据库表config配置类比如CORS跨域配置、拦截器配置common公共类比如统一返回结果、异常处理这个分层结构看着简单但能约束你写出可维护的代码。很多人一上来就把SQL直接写在Controller里项目小还好项目稍微一复杂就成灾难现场。application.yml的配置是个关键点。数据源配置要检查这几个参数spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/welcome_system?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的密码 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: Asia/Shanghai mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.welcome.entity configuration: map-underscore-to-camel-case: true注意map-underscore-to-camel-case这个配置数据库字段是student_name实体类是studentName没有这个配置的话查询结果就是null排查起来还挺容易忽略的。2.3 初始化Vue前端工程前端用Vue CLI创建项目npm install -g vue/cli vue create welcome-web创建时选上Router和Vuex包管理器选npm就行。进入项目目录后安装UI组件库和Axiosnpm install element-ui npm install axios这里有个细节要提醒Element UI只适配Vue 2如果创建项目时选了Vue 3需要改用Element PlusAPI上有些差异。做这个项目之前先确认好源码是哪个Vue版本再决定用哪套UI组件库。Vue工程的结构里src/router/index.js放路由配置src/api目录统一封装请求方法src/views放页面组件src/App.vue是根组件。建议在main.js里全局注册Element UI避免每个组件都import一遍import Vue from vue import ElementUI from element-ui import element-ui/lib/theme-chalk/index.css import App from ./App.vue import router from ./router import store from ./store Vue.use(ElementUI) new Vue({ router, store, render: h h(App) }).$mount(#app)3. 数据库设计与MyBatis实操3.1 核心表结构设计迎新系统的数据库表不用设计得太复杂但核心业务表必须覆盖全。我先说几个必备的表你在做设计时可以在此基础上扩展。新生信息表student这是整个系统的主表。字段包括学号、姓名、性别、身份证号、生源地、毕业中学、专业、学院、班级、联系方式、报到状态、是否缴费、宿舍ID等。学号建议做成唯一索引因为后续所有操作基本都是用学号关联。CREATE TABLE student ( id int(11) NOT NULL AUTO_INCREMENT, student_no varchar(20) NOT NULL COMMENT 学号, name varchar(50) NOT NULL COMMENT 姓名, gender tinyint(1) DEFAULT NULL COMMENT 性别 0女 1男, id_card varchar(18) DEFAULT NULL COMMENT 身份证号, origin varchar(100) DEFAULT NULL COMMENT 生源地, high_school varchar(100) DEFAULT NULL COMMENT 毕业中学, major varchar(50) DEFAULT NULL COMMENT 专业, college varchar(50) DEFAULT NULL COMMENT 学院, class_name varchar(50) DEFAULT NULL COMMENT 班级, phone varchar(11) DEFAULT NULL COMMENT 手机号, status tinyint(1) DEFAULT 0 COMMENT 报到状态 0未报到 1已报到, pay_status tinyint(1) DEFAULT 0 COMMENT 缴费状态 0未缴费 1已缴费, dormitory_id int(11) DEFAULT NULL COMMENT 宿舍ID, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_student_no (student_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT新生信息表;用户表sys_user存放系统登录账号角色分为管理员、辅导员、宿管等。密码一定要加密存储用MD5加盐或者BCrypt都可以。BCrypt更安全一些但MD5实现简单适合教学项目如果做真实上线的项目强烈建议用BCrypt。宿舍表dormitory宿舍楼号、房间号、床位数、已住人数。分配宿舍时的逻辑就是查一下哪个宿舍还有空床位床位加1同时把宿舍ID更新到新生表。报到记录表checkin_record新生现场报到时生成一条记录含学号、报到时间、操作人ID。这张表的作用是留痕后续如果要统计每个时间段的报到人数直接查这张表就行。其他的扩展表比如专业表、学院表、公告表看项目需求自行添加。我的建议是别一开始就把表设计得特别冗余把核心流程跑通后再逐步扩展这样更现实。3.2 MyBatis映射与分页查询实战MyBatis这块有三个点是项目中经常用到的也是面试常问的Mapper XML映射、动态SQL、分页插件。Mapper接口的写法很简单public interface StudentMapper { ListStudent selectByCondition(Param(name) String name, Param(college) String college, Param(status) Integer status); }对应的XML映射文件select idselectByCondition resultTypecom.example.welcome.entity.Student SELECT * FROM student where if testname ! null and name ! AND name LIKE CONCAT(%, #{name}, %) /if if testcollege ! null and college ! AND college #{college} /if if teststatus ! null AND status #{status} /if /where /select动态SQL里的where标签会自动处理掉第一个多余的AND这是我见过最实用的MyBatis语法之一解决了拼接SQL时最痛苦的问题。分页方面业界最常用的方案是PageHelper插件。要用的前提是先在pom.xml里引入依赖dependency groupIdcom.github.pagehelper/groupId artifactIdpagehelper-spring-boot-starter/artifactId version1.4.7/version /dependency然后在Service层调用时只需要在查询前加一行PageHelper.startPage(pageNum, pageSize); ListStudent list studentMapper.selectByCondition(name, college, status); PageInfoStudent pageInfo new PageInfo(list);PageInfo里包含了总记录数、总页数、当前页数据等完整信息直接返回给前端就能用。这里有个需要注意的点PageHelper.startPage()一定要写在Mapper查询方法的第一行附近中间不能间隔其他的SQL查询否则分页会作用到错误的SQL上。这个坑我踩过当时排查了大半天。4. 核心功能模块实现与关键代码4.1 登录认证与权限控制登录是管理系统第一个要做的功能。方案有很多从最简单的Session到JWT再到Spring Security JWT的完整方案。这个项目建议用JWT轻量而且前后端分离比较友好。后端实现思路是用户提交账号密码查询数据库比对密码对了就生成Token返回给前端。Token里带上用户ID和用户名还可以带上角色信息。后续每次请求前端把Token放在请求头里后端用一个拦截器统一校验。生成Token用io.jsonwebtoken这个库核心代码如下public String generateToken(Integer userId, String username, String role) { return Jwts.builder() .setSubject(username) .claim(userId, userId) .claim(role, role) .setExpiration(new Date(System.currentTimeMillis() 1000 * 60 * 60 * 24)) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); }关键点是SECRET_KEY要单独放在配置文件里别硬编码在代码中。过期时间设24小时对管理后台来说够用了。前端拿到Token后存在localStorage里每次请求用Axios拦截器带上axios.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization token } return config })后端拦截器校验Token无效就返回401。同时配合前端的路由守卫没有Token就直接跳回登录页router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.path ! /login !token) { next(/login) } else { next() } })这套组合拳打下来基本的登录认证和路由拦截就完整了。如果你还想要更细粒度的按钮级权限可以在路由meta里配置角色再做一层判断这里就不展开说了。4.2 新生信息导入与报表统计迎新场景下新生信息导入是个刚需功能。学校招办那边一般会给一个Excel表里面有几千上万个新生的基础信息总不能一个个手输进去。后端用EasyExcel或者POI来处理Excel。EasyExcel是阿里开源的内存占用更小操作也更简便。导入的流程是前端上传Excel文件后端读取每行数据做字段校验然后批量插入数据库。批量插入这块要特别注意性能。如果用MyBatis循环单条插入一万条数据可能要跑几分钟。正确的做法是用批处理// Service层 public void batchInsert(ListStudent students) { // 每500条刷一次批 SqlSession sqlSession sqlSessionFactory.openSession(ExecutorType.BATCH); StudentMapper mapper sqlSession.getMapper(StudentMapper.class); for (int i 0; i students.size(); i) { mapper.insert(students.get(i)); if (i % 500 0) { sqlSession.flushStatements(); } } sqlSession.commit(); }或者用XML里的foreach标签拼批量插入SQL一次插入500条实际效果也很好。统计功能推荐用ECharts在前端展示图表比如报到趋势折线图、各学院报到人数柱状图等。后端只需要提供统计数据接口比如按学院分组统计新生人数select idcountByCollege resultTypemap SELECT college AS name, COUNT(*) AS value FROM student GROUP BY college /select接口返回JSON数组前端ECharts直接吃这个数据格式。做数据可视化页面时你只需要稍微调整一下数据结构就能适配不同的图表类型这也是前端报表开发的基本功。4.3 宿舍分配与报到流程宿舍分配是迎新系统里比较有业务逻辑的功能。朴素版本的实现逻辑是根据新生的性别和专业找到匹配的宿舍楼然后查询该楼下有空床位的房间把新生分配进去。这里涉及多表查询用MyBatis来实现。先查符合条件的宿舍select idfindAvailableDormitory resultTypeDormitory SELECT * FROM dormitory WHERE building #{building} AND gender #{gender} AND occupied_num bed_num ORDER BY room_no LIMIT 1 /select查到后更新宿舍的已住人数同时更新学生的宿舍ID。为了保证并发下不错乱这里需要加事务控制把查询和更新放在同一个事务里Transactional(rollbackFor Exception.class) public void assignDormitory(Integer studentId) { Student student studentMapper.selectByPrimaryKey(studentId); Dormitory dormitory dormitoryMapper.findAvailableDormitory( student.getCollege(), student.getGender()); if (dormitory null) { throw new BusinessException(暂无可用宿舍请先手动安排); } dormitoryMapper.increaseOccupied(dormitory.getId()); studentMapper.updateDormitoryId(studentId, dormitory.getId()); }报到流程相对简单一些。新生到现场后工作人员输入学号系统展示新生信息确认身份后点击报到按钮更新状态为已报到同时写入报到记录。这里可以加一个打印功能报到完成后打印一份报到单很多高校现场都是这么操作的用前端window.print()或者引入一个打印插件就能实现。5. 前后端联调与常见问题排查5.1 跨域问题与接口联调前后端分开跑第一个要解决的就是跨域。前端跑在8080端口后端跑在8081端口浏览器会拦截跨域请求。后端配置一个CORS过滤器就行修改WebMvcConfigurerConfiguration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }开发时最简单的方法之一是用Vue CLI的代理配置在vue.config.js里设置module.exports { devServer: { proxy: { /api: { target: http://localhost:8081, changeOrigin: true } } } }这样前端请求/api/student/list开发服务器会自动转发到后端的http://localhost:8081/api/student/list浏览器层面感知不到跨域联调过程舒服很多。5.2 前端联调时前端页面常见的问题第一个要注意的是日期格式。后端返回的日期默认是yyyy-MM-dd HH:mm:ss如果你在application.yml里没配Jackson的date-format返回的可能是数组形式或者时间戳格式前端拿到后显示出来就会变成一串奇怪的数字。解决办法就是前面配置里写的统一格式化。第二个是接口返回的数据结构统一。我强烈建议前后端约定统一的返回格式{ code: 200, message: success, data: { } }后端用统一Result类包装前端在Axios响应拦截器里统一处理code。这样同一套逻辑处理成功和失败代码量会少很多也少了很多人为的坑。axios.interceptors.response.use( response { const res response.data if (res.code ! 200) { Message.error(res.message || 系统异常) return Promise.reject(new Error(res.message)) } return res }, error { Message.error(网络异常请稍后重试) return Promise.reject(error) } )5.3 MyBatis缓存问题与SQL执行慢排查MyBatis的缓存机制在面试里几乎必问但实际项目中反而容易坑到自己。一级缓存是SqlSession级别的默认开启二级缓存是Mapper级别的默认关闭。如果你在同一个SqlSession里先查了数据然后改了数据库表再查同一条件结果可能是缓存里的旧数据这就非常坑。实际开发中分布式环境下二级缓存会带来数据一致性问题我遇到很多团队干脆把它关了。关闭方法可以在application.yml里配置mybatis: configuration: cache-enabled: false如果你的某个查询比较慢第一步是先看SQL。MyBatis打印SQL的配置在application.yml里加上logging: level: com.example.welcome.mapper: debug这样控制台会打印Mapper执行的SQL和参数。看到SQL之后用EXPLAIN分析一下命中索引的情况。迎新系统里最常涉及的就是student_no、id_card这类字段记得建索引。有时候慢查询其实就是因为漏了索引全表扫描几万条数据自然就慢了。MyBatis的Update执行慢是另一个常见的问题。如果你的更新语句明明很简单但执行时间异常可以检查一下是不是更新条件没用上索引或者表数据量太大同时更新了太多行。5.4 数据库连接与中文乱码问题数据库连接这块最常见的坑是MySQL 8.x的驱动问题。如果你用的是MySQL 8.0com.mysql.jdbc.Driver已经废弃了必须用com.mysql.cj.jdbc.Driver同时URL要带时区参数这个前面已经提到了。中文乱码需要检查四个环节数据库表的字符集、连接URL的编码参数、后端代码的编码、前端页面的编码。数据库表建议统一用utf8mb4因为utf8mb4是完整的UTF-8实现支持表情符号连接URL里加useUnicodetruecharacterEncodingutf8。前后端通信层面SpringBoot默认UTF-8没问题前端页面在index.html里确认有charsetutf-8。这四个环节缺一不可。6. 项目上线部署与扩展思路这个系统用前后端分离架构部署方案可以灵活选择。传统方式是把后端打包成Jar包用java -jar命令跑前端打包成静态文件用Nginx托管。又或者用Docker把MySQL、后端、前端分别容器化用docker-compose编排实现一键部署。后端打包mvn clean package -DskipTests前端打包npm run build前端打包后的dist目录可以直接扔给Nginx。Nginx里面配置一个反向代理把/api开头的请求转发到后端的8081端口这样前端不用改任何代码server { listen 80; server_name your-domain.com; root /path/to/dist; index index.html; location /api/ { proxy_pass http://127.0.0.1:8081/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }部署的时候有几个细节要提前准备好。打包前检查一下application.yml里的数据库地址有没有从本地改成服务器地址前端axios请求的baseURL配合Nginx做相对路径配置。把这些处理好基本就没有太大问题了。关于这个项目的后续扩展方向我个人的想法是可以把新生报到流程做得更细比如对接人脸识别设备、自动贩卖机或者把缴费对接支付网关。SpringBoot的生态很成熟很多功能都可以往上面挂。如果做毕业设计可以再加一个请假审批流或者学生端小程序用uni-app写一个移动端页面项目完整度立刻就上去了。
返回列表