首页 / 资讯中心 / 文章详情

校园驿站管理系统毕设实战:Spring Boot+MyBatis-Plus从设计到部署

校园驿站管理系统毕设实战:Spring Boot+MyBatis-Plus从设计到部署 ★ FEATURED ARTICLE
简介这份资源是面向高校计算机专业学生的 Java 毕业设计与课程设计完整方案基于 SSM 框架开发的校园驿站管理系统适合需要完成期末大作业、毕业设计或想通过实战掌握 Java Web 开发的学习者。系统围绕快递驿站业务展开管理员可管理快递仓库、待发货与已收快递、物流及留言信息并维护员工和用户资料员工负责更新物流、发布留言用户则可签收快递、查看公告与物流状态功能划分清晰贴近真实业务场景。压缩包共约 2000 个文件涵盖 111 个 java 源码、82 个 jsp 页面、466 个 js 脚本、238 个 css 样式以及 sql 建库脚本、jar 依赖和 xml 配置等整体约 43.85MB代码附有注释便于阅读理解。资源同时提供万字报告文档、部署说明和 ppt已有 119 人学习下载后按说明即可在 JDK1.8 与 MySQL5.7 环境下部署运行适合作为高分毕设参考。1. 校园驿站管理系统从能跑到能过的 Java Web 毕设拆解每年到了三四月计算机专业的学生就开始为毕业设计发愁。选题太简单怕被答辩老师怼选题太复杂自己又搞不定。校园驿站管理系统这个题目恰好卡在一个很微妙的位置上——业务逻辑足够清晰技术栈覆盖了 Java Web 的核心知识点但又不至于像电商秒杀那样让人望而生畏。我见过不少同学在这个题目上翻车不是因为功能写不出来而是因为代码结构太乱、数据库设计经不起推敲、部署文档写得像天书最后答辩时被问得哑口无言。这篇文章面向的是正在做或者准备做这个题目的同学也适合那些想拿一个完整 Java Web 项目练手的新手。我会把校园驿站管理系统从需求拆解、技术选型、数据库设计、核心功能实现到部署上线的完整路径讲清楚重点放在那些真正影响你能否顺利通过答辩的细节上。不是泛泛而谈的科普而是你照着做就能跑起来的实操笔记。2. 技术选型与项目骨架为什么这套组合最稳2.1 后端为什么选 Spring Boot MyBatis-Plus 而不是原生 Servlet很多学校的 Java Web 课程还在教 Servlet JSP导致不少同学做毕设时第一反应就是用原生 Servlet 写。我的血泪经验是除非你们学校明确要求不能用框架否则千万别用原生 Servlet 硬写。原因很简单——原生 Servlet 的代码量至少是 Spring Boot 的三倍而且事务管理、参数校验、异常处理全要自己手写写到后面你会发现一半时间在写重复代码另一半时间在调那些莫名其妙的空指针。Spring Boot 的优势在于自动配置和起步依赖。你只需要在pom.xml里引入spring-boot-starter-web和mybatis-plus-boot-starter大部分基础设施就配好了。MyBatis-Plus 相比原生 MyBatis 又省去了大量单表 CRUD 的 XML 编写对于校园驿站这种以单表操作为主的系统来说开发效率提升非常明显。常见做法是三层架构Controller 层负责接收请求和返回响应Service 层处理业务逻辑Mapper 层负责数据库交互。这个分层不是摆设答辩老师经常会问你为什么要有 Service 层你要能说清楚Service 层是为了把业务逻辑从 Controller 里抽离出来方便复用和测试也避免 Controller 变得臃肿。2.2 前端选 Thymeleaf 还是前后端分离这是很多同学纠结的点。我的建议是如果你的前端基础一般或者时间比较紧直接用 Thymeleaf 做服务端渲染。原因有三第一不需要处理跨域问题第二不需要单独部署前端项目第三Session 管理更简单。校园驿站管理系统的用户角色不多学生、驿站管理员、系统管理员页面交互也不复杂Thymeleaf 完全够用。如果你已经熟悉 Vue 或者 React想做前后端分离也不是不行但你要额外处理跨域配置、Token 认证、前端路由权限控制这些问题。答辩的时候老师如果问你为什么用前后端分离你得能说出个所以然不能只说因为流行。2.3 项目骨架搭建的具体步骤下面是一个最小可运行的 Spring Boot 项目结构你可以直接照着建campus-station/ ├── src/main/java/com/example/station/ │ ├── StationApplication.java │ ├── controller/ │ ├── service/ │ ├── mapper/ │ ├── entity/ │ └── config/ ├── src/main/resources/ │ ├── application.yml │ ├── mapper/ │ └── templates/ └── pom.xmlpom.xml的核心依赖如下dependencies !-- Web 场景启动器包含 Tomcat 和 Spring MVC -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Thymeleaf 模板引擎用于服务端渲染页面 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-thymeleaf/artifactId /dependency !-- MyBatis-Plus简化单表 CRUD -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency !-- MySQL 驱动 -- dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency !-- Lombok减少 getter/setter 样板代码 -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependenciesapplication.yml的关键配置server: port: 8080 # 默认端口如果被占用改成 8081 spring: datasource: url: jdbc:mysql://localhost:3306/campus_station?useUnicodetruecharacterEncodingutf-8serverTimezoneAsia/Shanghai username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver thymeleaf: cache: false # 开发阶段关闭缓存改页面不用重启 mybatis-plus: mapper-locations: classpath:mapper/*.xml # XML 映射文件位置 configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印 SQL方便调试这里有几个参数值得注意。serverTimezoneAsia/Shanghai不加的话MySQL 8 以上版本会报时区错误。cache: false在开发阶段一定要加否则你改了 HTML 页面刷新浏览器看不到变化会以为代码写错了。log-impl配置成 StdOutImpl 后每次数据库操作都会在控制台打印 SQL 语句调 bug 的时候非常有用。提示数据库连接池默认用的是 HikariCPSpring Boot 2.x 以后自带。如果你的项目并发量不大不需要额外配置连接池参数。3. 数据库设计五张核心表撑起整个系统3.1 表结构设计与字段说明校园驿站管理系统的核心业务其实就三件事包裹入库、包裹出库、用户取件。围绕这三件事我一般会设计五张表用户表、驿站表、包裹表、取件记录表、管理员表。下面给出建表 SQL你可以直接复制到 MySQL 里执行。-- 用户表存储学生和教职工信息 CREATE TABLE user ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键, username varchar(50) NOT NULL COMMENT 学号/工号, password varchar(100) NOT NULL COMMENT 密码加密存储, real_name varchar(50) DEFAULT NULL COMMENT 真实姓名, phone varchar(20) DEFAULT NULL COMMENT 手机号, role tinyint DEFAULT 0 COMMENT 角色0-学生1-驿站管理员2-系统管理员, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; -- 驿站表支持多驿站管理 CREATE TABLE station ( id bigint NOT NULL AUTO_INCREMENT, name varchar(100) NOT NULL COMMENT 驿站名称, address varchar(200) DEFAULT NULL COMMENT 详细地址, manager_id bigint DEFAULT NULL COMMENT 负责管理员ID, status tinyint DEFAULT 1 COMMENT 状态0-停用1-正常, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT驿站表; -- 包裹表核心业务表 CREATE TABLE package ( id bigint NOT NULL AUTO_INCREMENT, tracking_no varchar(50) NOT NULL COMMENT 快递单号, user_id bigint NOT NULL COMMENT 收件人ID, station_id bigint NOT NULL COMMENT 所在驿站ID, shelf_code varchar(20) DEFAULT NULL COMMENT 货架编号, status tinyint DEFAULT 0 COMMENT 状态0-待取件1-已取件2-已退回, in_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 入库时间, out_time datetime DEFAULT NULL COMMENT 出库时间, PRIMARY KEY (id), UNIQUE KEY uk_tracking (tracking_no), KEY idx_user (user_id), KEY idx_station (station_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT包裹表; -- 取件记录表用于追溯和统计 CREATE TABLE pickup_record ( id bigint NOT NULL AUTO_INCREMENT, package_id bigint NOT NULL COMMENT 包裹ID, operator_id bigint DEFAULT NULL COMMENT 操作员ID, pickup_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 取件时间, remark varchar(200) DEFAULT NULL COMMENT 备注, PRIMARY KEY (id), KEY idx_package (package_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT取件记录表;这里有几个设计决策需要解释。tracking_no加了唯一索引因为快递单号理论上不会重复这样能防止同一条快递被重复录入。package表的status字段用 tinyint 而不是 varchar是为了查询效率但你要在代码里用枚举或者常量类来管理这些状态值不要直接在代码里写魔法数字。pickup_record表单独拆出来而不是直接在package表里加字段是为了保留完整的取件历史方便后续做统计报表。3.2 用 MyBatis-Plus 生成基础代码MyBatis-Plus 提供了一个代码生成器可以根据表结构自动生成 Entity、Mapper、Service、Controller 的骨架代码。虽然生成的代码不能直接用但能省去大量重复劳动。下面是一个最小化的生成器配置public class CodeGenerator { public static void main(String[] args) { // 数据源配置 DataSourceConfig dsc new DataSourceConfig.Builder( jdbc:mysql://localhost:3306/campus_station?serverTimezoneAsia/Shanghai, root, 你的密码 ).build(); // 全局配置 GlobalConfig gc new GlobalConfig.Builder() .outputDir(System.getProperty(user.dir) /src/main/java) .author(你的名字) .disableOpenDir() // 生成后不自动打开文件夹 .build(); // 包配置 PackageConfig pc new PackageConfig.Builder() .parent(com.example.station) .entity(entity) .mapper(mapper) .service(service) .controller(controller) .build(); // 策略配置指定要生成的表 StrategyConfig sc new StrategyConfig.Builder() .addInclude(user, station, package, pickup_record) .entityBuilder() .enableLombok() // 使用 Lombok 注解 .build(); new AutoGenerator(dsc).global(gc).packageInfo(pc).strategy(sc).execute(); } }这段代码执行后会在你指定的包路径下生成对应的 Java 文件。生成的 Entity 类会带上TableName和TableId注解Mapper 接口会继承BaseMapperTService 接口会继承IServiceT。你只需要在生成的代码基础上补充业务逻辑即可。注意代码生成器依赖mybatis-plus-generator和freemarker两个额外的依赖记得在pom.xml里加上否则会报 ClassNotFoundException。4. 核心功能实现包裹入库、取件与权限拦截4.1 包裹入库的完整代码链路包裹入库是整个系统最核心的功能。业务流程是管理员扫描或输入快递单号系统根据单号自动匹配收件人这里简化处理实际场景可能需要对接快递公司 API然后分配货架号最后写入数据库。先看 Controller 层的代码Controller RequestMapping(/package) public class PackageController { Autowired private PackageService packageService; // 跳转到入库页面 GetMapping(/in) public String toInPage(Model model) { // 查询所有正常状态的驿站供下拉选择 model.addAttribute(stations, stationService.listActive()); return package/in; } // 处理入库请求 PostMapping(/in) ResponseBody public Result inPackage(RequestBody PackageInDTO dto) { // 参数校验快递单号不能为空 if (StringUtils.isBlank(dto.getTrackingNo())) { return Result.fail(快递单号不能为空); } // 调用 Service 层处理业务 return packageService.inPackage(dto); } }Service 层的实现Service public class PackageServiceImpl extends ServiceImplPackageMapper, Package implements PackageService { Autowired private UserService userService; Override Transactional(rollbackFor Exception.class) // 开启事务 public Result inPackage(PackageInDTO dto) { // 1. 根据手机号后四位或学号匹配收件人 User user userService.findByPhoneOrUsername(dto.getReceiverKey()); if (user null) { return Result.fail(未找到收件人请确认学号或手机号); } // 2. 检查快递单号是否已存在 Long count lambdaQuery() .eq(Package::getTrackingNo, dto.getTrackingNo()) .count(); if (count 0) { return Result.fail(该快递单号已入库请勿重复操作); } // 3. 构建包裹实体并保存 Package pkg new Package(); pkg.setTrackingNo(dto.getTrackingNo()); pkg.setUserId(user.getId()); pkg.setStationId(dto.getStationId()); pkg.setShelfCode(dto.getShelfCode()); pkg.setStatus(0); // 0 表示待取件 save(pkg); return Result.success(入库成功); } }这段代码有几个关键点。Transactional注解保证了入库操作的原子性如果保存包裹时出错前面的查询操作不会留下脏数据。lambdaQuery()是 MyBatis-Plus 提供的链式查询方法比手写 SQL 更安全能防止 SQL 注入。状态值0建议用常量类管理比如PackageStatus.WAITING这样代码可读性更好。4.2 取件流程与并发问题处理取件流程比入库多了一个并发问题如果两个管理员同时操作同一个包裹的出库可能会导致重复取件。常见的做法是在 Service 层加乐观锁或者用数据库的行锁。Override Transactional(rollbackFor Exception.class) public Result pickup(Long packageId, Long operatorId) { // 使用 select ... for update 锁定该行防止并发取件 Package pkg baseMapper.selectByIdForUpdate(packageId); if (pkg null) { return Result.fail(包裹不存在); } if (pkg.getStatus() ! 0) { return Result.fail(该包裹已取件或已退回); } // 更新包裹状态 pkg.setStatus(1); pkg.setOutTime(new Date()); updateById(pkg); // 写入取件记录 PickupRecord record new PickupRecord(); record.setPackageId(packageId); record.setOperatorId(operatorId); record.setPickupTime(new Date()); pickupRecordService.save(record); return Result.success(取件成功); }对应的 Mapper XMLselect idselectByIdForUpdate resultTypecom.example.station.entity.Package SELECT * FROM package WHERE id #{id} FOR UPDATE /selectFOR UPDATE是 MySQL 的行级锁能在事务提交前锁住这条记录其他事务必须等待。对于校园驿站这种并发量不大的场景这个方案完全够用。如果你不想用行锁也可以在package表加一个version字段做乐观锁但代码会稍微复杂一点。4.3 登录拦截与角色权限控制校园驿站管理系统有三种角色学生、驿站管理员、系统管理员。不同角色能访问的页面不同需要在拦截器里做判断。Component public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行登录页和静态资源 String uri request.getRequestURI(); if (uri.contains(/login) || uri.contains(/css) || uri.contains(/js) || uri.contains(/images)) { return true; } // 检查 Session 中是否有用户信息 Object user request.getSession().getAttribute(loginUser); if (user null) { response.sendRedirect(/login); return false; } return true; } }注册拦截器的配置类Configuration public class WebConfig implements WebMvcConfigurer { Autowired private LoginInterceptor loginInterceptor; Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) .addPathPatterns(/**) // 拦截所有路径 .excludePathPatterns(/login, // 排除登录相关 /doLogin, /css/**, /js/**); } }角色权限的控制可以放在 Controller 方法里用自定义注解加 AOP 实现也可以在拦截器里根据 URI 前缀判断。对于毕设来说最简单的做法是在拦截器里判断/admin/**开头的路径只有系统管理员能访问/station/**开头的路径只有驿站管理员能访问。5. 避坑指南那些答辩前夜让我抓狂的问题5.1 中文乱码从数据库到页面的全链路排查现象入库时输入的收件人姓名在数据库里显示正常但页面上显示成问号。原因通常是三个环节出了问题——数据库字符集、JDBC 连接参数、Thymeleaf 页面编码。数据库建表时要用utf8mb4JDBC URL 要加characterEncodingutf-8HTML 页面要声明meta charsetUTF-8。三个地方缺一个都可能乱码。解决先执行SHOW VARIABLES LIKE character%;确认数据库字符集。如果是latin1需要修改 MySQL 配置文件或者重新建库。JDBC URL 里的characterEncoding参数不能省。Thymeleaf 模板文件本身也要保存为 UTF-8 编码IDEA 右下角可以改。5.2 静态资源 404Spring Boot 的默认路径规则现象CSS 和 JS 文件放在src/main/resources/static/下但页面加载时 404。原因Spring Boot 默认会把static/目录下的文件映射到根路径。如果你在 HTML 里写的是/static/css/style.css就会 404因为正确的路径是/css/style.css。解决要么改 HTML 里的引用路径去掉/static前缀要么在application.yml里配置spring.mvc.static-path-pattern。我一般推荐第一种因为这是 Spring Boot 的约定改配置反而容易让后面接手的人困惑。5.3 MyBatis-Plus 逻辑删除与唯一索引冲突现象删除了一个用户后想用相同的学号重新注册报唯一索引冲突。原因MyBatis-Plus 的逻辑删除只是把deleted字段置为 1记录还在表里。而username字段的唯一索引还在生效所以新插入相同学号会冲突。解决如果确实需要逻辑删除唯一索引要改成联合索引把deleted字段加进去。或者干脆用物理删除对于毕设来说物理删除更简单也不容易出问题。答辩老师一般不会深究你用逻辑删除还是物理删除但如果你用了逻辑删除又没处理好唯一索引被问到会很尴尬。5.4 事务不生效自调用导致的代理失效现象在 Service 类的 A 方法里直接调用本类的 B 方法B 方法上的Transactional没起作用。原因Spring 的事务是基于 AOP 代理实现的自调用走的是原始对象的方法不经过代理所以事务注解失效。解决把 B 方法抽到另一个 Service 类里或者通过AopContext.currentProxy()获取代理对象再调用。最简单的做法是拆分 Service让每个 Service 只负责自己的事务边界。5.5 部署到服务器后连接数据库超时现象本地跑得好好的部署到云服务器后启动报连接超时。原因云服务器的安全组没有开放 3306 端口或者 MySQL 只允许 localhost 连接。解决在云服务器控制台开放 3306 端口生产环境不建议直接暴露可以只开放给应用服务器 IP。MySQL 的user表里要有一条host为%的记录允许远程连接。执行CREATE USER root% IDENTIFIED BY 密码;和GRANT ALL ON *.* TO root%;即可。当然更安全的做法是只允许特定 IP 连接。6. 部署上线与答辩演示的加分技巧部署这一步很多同学会卡在环境配置上。我的习惯是本地用 Windows 开发部署用 Linux 服务器但数据库版本和 JDK 版本尽量保持一致。本地 JDK 17服务器也装 JDK 17本地 MySQL 8.0服务器也装 8.0。版本不一致导致的玄学问题排查起来非常痛苦。打包命令很简单# 跳过测试打包生成可执行 jar mvn clean package -DskipTests # 后台启动日志输出到 app.log nohup java -jar campus-station-1.0.jar app.log 21 启动后先看日志有没有报错再访问http://服务器IP:8080确认页面能打开。如果打不开先检查防火墙和安全组再检查应用是否真的启动了ps -ef | grep java。答辩演示的时候我建议你提前准备好三样东西一份数据库 ER 图、一份核心接口的时序图、一份部署架构图。不用画得多漂亮用 draw.io 或者 ProcessOn 画清楚就行。老师看到你有这些准备印象分会高很多。另外演示的时候不要只点页面要打开控制台让老师看到 SQL 日志证明你的代码是真的在操作数据库不是静态页面糊弄人。还有一个容易被忽略的点PPT 里不要堆大段代码。把核心代码截取关键几行配上注释和说明就够了。老师更关心的是你的设计思路和解决问题的能力而不是你写了多少行代码。如果你在答辩时能主动说出这个地方我一开始用了 XX 方案后来发现有问题改成了 YY 方案这比单纯展示功能更有说服力。最后说一个我自己的习惯每次改完代码不管多小的改动都先本地跑一遍主流程。包裹入库、取件、查询这三个操作走通了再提交代码。很多同学改了一个小地方觉得没问题就直接部署结果演示的时候当场翻车。这种后悔药没地方买只能靠多跑一遍来避免。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站