最近帮好几个朋友看过 Springboot 旅游管理系统源码,这个类型的项目在毕业设计和课程设计里真的非常常见。说句实在话,压缩包里的程序、数据库脚本、部署文档、论文模板基本都是全的,但多数人拿到手之后第一脚就踩坑——要么数据库连不上,要么版本对不上,要么启动起来之后页面一片空白。旅游管理系统听起来不算大,但背后涉及用户、景点、线路、订单、评论、收藏一堆业务,再叠加 Spring Boot 后端和前端页面的配合,第一次接触的人确实容易卡住。
这篇东西不是我凭空在这里讲原理,而是把这类项目从业务梳理、技术选型、数据库设计、核心模块实现、调试部署到论文整理这一整条链路重新捋一遍。适合的人群有两类:第一类是刚拿到 Springboot 旅游管理系统源码但还没跑起来的人,第二类是准备自己从零写一个类似系统、但不知道从哪下手的同学。我会尽量按一个实际开发者的操作顺序来讲,每一步为什么这么做、常见怎么翻车、怎么快速解决,都展开聊。
1. 先把旅游管理系统的业务闭环盘清楚:角色、主线和功能清单
1.1 系统里有哪两类角色,四条核心业务主线是什么
很多人犯的第一个错误,是拿到代码就急着点运行。我不是反对先跑起来,但如果你连这个系统是干什么的都没弄明白,后面改需求、加功能、调 bug 的时候会非常痛苦。建议先花半小时把业务逻辑盘清楚。
旅游管理系统的本质,是把“游客找景点、选线路、订门票酒店、游玩后评价”这件事搬到线上。角色一般就两类:
- 管理员:维护景点、线路、酒店等基础数据,处理订单和评论,管理用户。
- 普通用户:注册登录、浏览景点和线路、搜索筛选、下单预订、收藏点赞、发表评论。
围绕这两个角色,业务主线可以整理成四条:
- 内容线:管理员录入景点、线路、酒店信息,用户在前台浏览和搜索。
- 交易线:用户对景点门票、线路或酒店下单,订单状态从待处理流转到已确认、已完成或已取消。
- 互动线:用户对景点收藏、点赞、评论、打分,管理员可以回复评论。
- 管理线:管理员对全部数据进行增删改查,以及基础的统计和上下架控制。
这四条线一清楚,后面数据库表怎么设计、Controller 怎么拆分、前端页面怎么组织,基本上就是顺水推舟的事。我见过很多代码本身没有任何问题,但因为开发之前没把业务想清楚,导致功能对不上需求,答辩时被老师一问就露馅。
1.2 功能清单怎么列才不返工
我建议拿到项目之后,先按模块、角色、功能点、核心表这四个维度列一张功能清单。这张清单既是你看代码的索引,也是后面写论文时需求分析章节的底稿。
| 模块 | 角色 | 功能点 | 核心表 |
|---|---|---|---|
| 登录注册 | 用户/管理员 | 注册、登录、退出 | sys_user |
| 景点管理 | 管理员 | 景点增删改查、上下架、分类维护 | scenic_spot |
| 景点展示 | 用户 | 分页列表、按名称/城市筛选、详情查看 | scenic_spot |
| 线路管理 | 管理员 | 线路增删改查、关联景点、价格维护 | travel_route |
| 订单管理 | 用户/管理员 | 创建订单、状态流转、后台确认/取消 | order_info |
| 评论管理 | 用户/管理员 | 发布评论、回复、删除 | comment |
| 收藏管理 | 用户 | 收藏/取消收藏、我的收藏列表 | favorite |
| 统计概览 | 管理员 | 景点数量、订单数量、简单趋势 | 聚合查询 |
表格列完之后,再对照源码里的 Controller 和 Service,你会发现绝大多数接口其实就是对某一张表的增删改查,外面包了一层参数校验和业务判断。有了这张表,你就不会在整个项目里漫无目的地搜索“这个功能在哪”了。
2. 技术选型为什么是这个组合:Spring Boot 与 MyBatis Plus 的取舍
2.1 这套技术栈在毕设和课设里的优势
Springboot 旅游管理系统这类项目,默认组合基本是:后端 Spring Boot + MyBatis Plus,数据库 MySQL,前端 Vue + Element UI 或者 Thymeleaf 模板,鉴权用 JWT 或 Session,工具链是 Maven、IDEA、Navicat。
为什么这个组合几乎是标准答案?因为它每个环节都踩在“够用且省事”的点上。
Spring Boot 解决了传统 SSM 项目里大量配置文件的问题,内嵌 Tomcat,一条java -jar就能把服务跑起来。MyBatis Plus 又比原生 MyBatis 省掉了一大堆 XML 的编写,单表操作基本不用手写 SQL,分页查询自带 Page 对象,开发效率能提升不少。MySQL 免费、资料多,绝大多数运行时报错都能在搜索引擎找到现成答案。前端如果选 Vue + Element UI,后台管理界面基本是靠组件拼出来的,对非前端专业的开发者非常友好。
当然也有同学问过:要不要上 Spring Cloud、要不要加 Redis、要不要换 JPA。我的看法很直接:旅游管理系统这个体量,单 Spring Boot 完全够。Spring Cloud 那套微服务治理对这个项目来说纯属给自己加戏,Redis 做缓存能加分但没到缺了它不行的程度,JPA 也能用,但 MyBatis Plus 的容错率更高,出了问题好定位。
2.2 分层结构的约定和工程目录
不管你拿到的源码目录长成什么样,我都建议按照下面这种分层思路去理解它。
src/main/java/com/example/travel ├── config // 配置类:拦截器、跨域、WebMvc配置 ├── controller // 接口层:接收参数、返回统一结果 ├── service // 业务层:核心逻辑都在这里 │ └── impl ├── mapper // MyBatis Plus 的 Mapper 接口 ├── entity // 数据库表对应的实体类 ├── dto // 前端传入的参数对象 ├── vo // 返回给前端的数据对象 ├── common // 统一返回结果、全局异常、常量 └── utils // JWT、日期、文件上传等工具类分层的核心价值在于出了问题能快速定位:Controller 层报错就去 Controller 找,逻辑不对去 Service 查,SQL 有问题再看 Mapper。很多人代码能力和项目理解都不差,但最怕遇到目录完全乱铺的源码,那才叫真的无从下手。
2.3 关键依赖和版本匹配
pom.xml 里几个关键依赖,我按比较稳的组合列一下:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> <relativePath/> </parent> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.5</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency>我这里给的是 Spring Boot 2.7.x 的组合。特别提醒一句:Spring Boot 3.x 要求 JDK 17,而且包名从 javax 迁移到了 jakarta,如果你拿到的源码是 Spring Boot 2.x 写的,千万别图新鲜直接升 3.x,不然光改 import 就能改到崩溃。MyBatis Plus 也分版本,3.5.x 对 Spring Boot 2 支持最好,别选错 starter。
3. 数据库是系统的地基:核心表的字段设计和建表细节
3.1 用户表和景点表:一切业务围绕它们转
数据库表结构决定开发效率,这句话不是空话。后端代码再漂亮,如果表设计不合理,联表查询、字段拼接会搞得你痛不欲生。旅游业系统里,用户表和景点表是基础。
用户表(sys_user)我建议至少包含这些字段:
CREATE TABLE `sys_user` ( `id` bigint NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL COMMENT '用户名', `password` varchar(100) NOT NULL COMMENT '密码,加密存储', `nickname` varchar(50) DEFAULT NULL COMMENT '昵称', `phone` varchar(20) DEFAULT NULL COMMENT '手机号', `email` varchar(100) DEFAULT NULL COMMENT '邮箱', `avatar` varchar(255) DEFAULT NULL COMMENT '头像地址', `role` tinyint DEFAULT '1' COMMENT '角色:0管理员 1普通用户', `status` tinyint DEFAULT '1' COMMENT '状态:0禁用 1正常', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';有几个细节是很多教程不会单独讲的。密码绝对不要明文存,至少 MD5 加盐,有条件直接用 BCrypt。username 必须加唯一索引,不然注册的时候可能出现重复账号。create_time 和 update_time 让数据库自动维护,代码里就不用每次手动 set。
景点表(scenic_spot)是旅游系统的核心资产,几乎所有功能都围绕它展开:
CREATE TABLE `scenic_spot` ( `id` bigint NOT NULL AUTO_INCREMENT, `name` varchar(100) NOT NULL COMMENT '景点名称', `city` varchar(50) DEFAULT NULL COMMENT '所在城市', `category` varchar(50) DEFAULT NULL COMMENT '分类:自然/人文/主题乐园等', `description` text COMMENT '景点简介', `address` varchar(200) DEFAULT NULL COMMENT '详细地址', `price` decimal(10,2) DEFAULT '0.00' COMMENT '门票价格', `open_time` varchar(100) DEFAULT NULL COMMENT '开放时间', `cover_image` varchar(255) DEFAULT NULL COMMENT '封面图片地址', `images` text COMMENT '多图地址,JSON数组或逗号分隔', `visit_count` int DEFAULT '0' COMMENT '浏览量', `status` tinyint DEFAULT '1' COMMENT '状态:0下架 1上架', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='景点表';这里有一个看起来很非主流但其实很省事的做法:images 字段直接用 JSON 字符串存多张图片。很多初学者为了“规范化”单独建一张景点图片子表,结果查询一次景点要两次连表,删除景点还要记得清理子表,复杂度翻倍。对于毕设和课设体量的系统,JSON 字符串存储完全够用。
3.2 订单表和评论表:交易链路与用户反馈
订单表(order_info)是交易链路的中心,核心字段要能回答这几个问题:谁下的单?买了什么?花了多少钱?现在什么状态?
CREATE TABLE `order_info` ( `id` bigint NOT NULL AUTO_INCREMENT, `order_no` varchar(32) NOT NULL COMMENT '订单编号', `user_id` bigint NOT NULL COMMENT '下单用户ID', `spot_id` bigint DEFAULT NULL COMMENT '景点ID', `route_id` bigint DEFAULT NULL COMMENT '线路ID,与spot_id按类型二选一', `order_type` tinyint DEFAULT '1' COMMENT '类型:1门票 2线路 3酒店', `price` decimal(10,2) NOT NULL COMMENT '订单金额', `quantity` int DEFAULT '1' COMMENT '数量', `status` tinyint DEFAULT '0' COMMENT '状态:0待处理 1已确认 2已完成 3已取消', `real_name` varchar(50) DEFAULT NULL COMMENT '联系人', `phone` varchar(20) DEFAULT NULL COMMENT '联系电话', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_user_id` (`user_id`), UNIQUE KEY `uk_order_no` (`order_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';设计订单表的常见纠结在于:一个订单可能买门票,也可能订线路或酒店,到底建一张表还是拆成三张表?务实做法是用 order_type 区分类型,spot_id 和 route_id 按类型二选一。拆表看起来很规范,但会让查询、统计、后台管理变得很分裂。一张订单表加类型字段,是这个体量系统的最优解。
评论表(comment)相对简单:
CREATE TABLE `comment` ( `id` bigint NOT NULL AUTO_INCREMENT, `user_id` bigint NOT NULL COMMENT '用户ID', `spot_id` bigint NOT NULL COMMENT '景点ID', `content` varchar(500) DEFAULT NULL COMMENT '评论内容', `score` tinyint DEFAULT '5' COMMENT '评分1-5', `reply_content` varchar(500) DEFAULT NULL COMMENT '管理员回复', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_spot_id` (`spot_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='评论表';3.3 建表时容易忽略的四个坑
第一,字符集。MySQL 5.7 以下默认 latin1,存中文直接变问号。建表和建库统一用 utf8mb4,它比 utf8 多支持 emoji,一劳永逸。建库语句:
CREATE DATABASE travel_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;第二,物理外键。教科书喜欢强调外键,但实际项目中外键很多时候是绊脚石。删除一个景点如果订单表有引用,外键会直接拦截删除操作,报错信息还很含糊。我的习惯是:表之间用字段逻辑关联,不建物理外键。源码里如果建了外键导致删除失败,先检查并去掉它。
第三,价格用 decimal,别用 float。float 做金额计算有精度问题,0.1 加 0.2 都能算出 0.30000000000000004,这在账单显示上非常尴尬。decimal(10,2) 是标准做法。
第四,自动维护时间字段。create_time 和 update_time 两条字段建议让 MySQL 自动填充和更新,代码里不要手动 set,否则数据对不上会很难排查。
4. 从登录到下单:四个核心模块的代码实现思路
4.1 登录鉴权:JWT 方案还是 Session 方案
先回答最关键的问题:如果前后端是分离的,推荐 JWT;如果项目用了 Thymeleaf 模板渲染,Session 完全够用。怎么判断?看前端的请求是 fetch/axios 调接口还是直接页面跳转。
JWT 的工作流程就是三步:登录成功后基于用户信息生成 token 返回前端;前端把 token 存起来,每次请求放到 Authorization 头里;后端写一个拦截器,解析 token 并校验,通过就放行,不通过就返回 401。
核心代码:
// 生成 token public String generateToken(Long userId, String role, String secret, long expire) { return Jwts.builder() .claim("userId", userId) .claim("role", role) .setSubject(String.valueOf(userId)) .setExpiration(new Date(System.currentTimeMillis() + expire)) .signWith(SignatureAlgorithm.HS256, secret) .compact(); } // 拦截器解析 token public Long getUserIdFromToken(String token, String secret) { Claims claims = Jwts.parser() .setSigningKey(secret) .parseClaimsJws(token) .getBody(); return claims.get("userId", Long.class); }有一个容易被忽略但答辩容易问到的点:JWT 的 secret 要放进配置文件,别硬编码在代码里。硬编码了项目也能跑,但这属于一眼看出来的坏习惯。另外过期时间建议设成 24 小时,太短用户体验差,太长不安全。
4.2 景点检索与分页:一个接口走天下
景点列表是用户最常用、也是前台最核心的功能。多数系统需要支持按关键字搜索名称、按城市筛选、按分类筛选,再加分页。
用 MyBatis Plus 的 LambdaQueryWrapper 可以写得很简洁:
public Page<ScenicSpot> searchSpot(String keyword, String city, Long pageNum, Long pageSize) { Page<ScenicSpot> page = new Page<>(pageNum, pageSize); LambdaQueryWrapper<ScenicSpot> wrapper = new LambdaQueryWrapper<>(); wrapper.like(StringUtils.isNotBlank(keyword), ScenicSpot::getName, keyword) .eq(StringUtils.isNotBlank(city), ScenicSpot::getCity, city) .eq(ScenicSpot::getStatus, 1) .orderByDesc(ScenicSpot::getVisitCount); return spotMapper.selectPage(page, wrapper); }这里最关键的是 like 和 eq 前面那个 boolean 参数:前端不传对应条件时,这个条件自动失效,不需要写一堆 if-else 拼接 wrapper。很多初学者不明白这个设计,写出来的代码又长又容易错。
分页参数建议固定用 pageNum 和 pageSize,不要一个接口用 page、另一个接口用 current,前后端对不上非常痛苦。返回结果建议统一格式,data 里放 records 和 total,前端表格组件直接绑定即可。
4.3 订单状态流转:简单状态机比谁都改状态强
订单是旅游系统里业务约束最多的部分。我建议状态控制在四个以内:待处理、已确认、已完成、已取消。
流转规则大概是这样:
- 用户下单,生成待处理订单。
- 管理员在后台确认,变成已确认。
- 游玩结束或默认时间到,改为已完成。
- 用户取消、管理员拒绝、超时未支付,变为已取消。
代码里我最推荐的做法是写一个带状态校验的 transform 方法,而不是让前端想传什么状态就传什么状态:
public boolean transformOrderStatus(Long orderId, Integer currentStatus, Integer targetStatus) { OrderInfo order = orderMapper.selectById(orderId); if (order == null) { throw new BusinessException("订单不存在"); } if (!order.getStatus().equals(currentStatus)) { throw new BusinessException("订单状态已变化,请刷新后重试"); } OrderInfo update = new OrderInfo(); update.setId(orderId); update.setStatus(targetStatus); return orderMapper.updateById(update) > 0; }currentStatus 不是随便传的,而是后端从数据库查出来的真实状态,再和目标状态做比对。这样能最大程度避免两个人同时操作同一个订单导致的状态错乱。
顺带提一个加分点:如果旅游系统涉及票务库存,比如景点门票有数量限制,下单扣库存时建议用乐观锁:
UPDATE scenic_spot SET stock = stock - 1 WHERE id = #{spotId} AND stock > 0;影响行数为 0 就说明库存不足。这种写法比先查后改安全得多,而且答辩时提“乐观锁解决库存超卖”,是很加分的点。
5. 调试部署实录:从本地跑通到服务器发布的全过程
5.1 环境准备:版本匹配是第一道坎
标题里特意提到“调试部署”,这个环节确实是拿到源码后卡住最多人的地方。我按自己实际操作的完整链路走一遍。
本地开发环境推荐这样配:
| 软件 | 推荐版本 | 理由 |
|---|---|---|
| JDK | 1.8 或 11 | Spring Boot 2.x 两者兼容,默认 1.8 最稳 |
| Maven | 3.6 以上 | 太老版本存在依赖解析问题 |
| MySQL | 5.7 或 8.0 | 推荐 8.0,驱动用对应 8.x 版本 |
| IDEA | 2021.2 以上 | Spring Boot 支持已经很成熟 |
| Navicat | 任意新版 | 导入 SQL、执行脚本方便 |
第一步,在 IDEA 里打开项目,等 Maven 把依赖全部下载完。这一步最考验耐心,网络不好或镜像源没配,会长时间卡在下载界面。建议在 Maven 的 settings.xml 里配置阿里云镜像,这算是国内开发的必修课。
第二步,找到项目里的 SQL 脚本,在 Navicat 里新建数据库并执行脚本。执行之前先看脚本开头有没有 CREATE DATABASE 语句,如果有,数据库名必须和配置文件的连接地址保持一致。
第三步,修改 application.yml 里的数据库连接。重点检查 url、用户名、密码:
spring: datasource: url: jdbc:mysql://localhost:3306/travel_system?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: 123456第四步,启动 Application 主类。启动成功后会看到 Tomcat 端口打印,比如 Tomcat started on port(s) 8080。
5.2 配置文件里最容易踩的三个坑
第一个坑:数据库时区。MySQL 8.0 默认时区可能和本地不一致,连接串里不写 serverTimezone=Asia/Shanghai,启动会直接报时区相关的错误。这个参数必须加,位置就在 url 末尾。
第二个坑:端口冲突。Tomcat 默认 8080,如果机器上已经有一个服务占了 8080,启动会报 Port already in use。解决办法是换端口或杀掉占用进程。换端口:
server: port: 9090Windows 查端口占用:netstat -ano | findstr 8080,然后taskkill /PID 进程号 /F。Linux 上对应的是lsof -i:8080和kill -9 进程号。
第三个坑:上传文件路径。很多旅游系统涉及图片上传,代码里如果写了绝对路径比如D:/upload,那只是原作者电脑的路径,换电脑不改成自己的路径,上传功能必定报错。拿到源码之后先在项目里全局搜一遍所有带盘符的路径,全部改成自己的本地目录。
5.3 打包与部署的完整命令
本地跑通之后,部署到服务器一般是打 jar 包。项目根目录执行:
mvn clean package -DskipTeststarget 目录下会生成 jar 包,比如 travel-system-0.0.1.jar。上传到服务器之后,后台运行:
nohup java -jar travel-system-0.0.1.jar --server.port=8080 > app.log 2>&1 &把这行命令的参数拆开解释:nohup 是让程序在 SSH 断开之后继续跑;> app.log 是把日志输出到文件;2>&1 是把错误输出也写进同一个文件;& 表示后台运行。以后想看日志随时tail -f app.log。
部署前记得检查服务器上的 MySQL 是否已经建好库并导入数据。服务器密码不要求和本地一样,但是改完密码必须同步改 jar 包旁边的配置文件。另外服务器防火墙要放行对应端口,云服务器还要在安全组里额外加一条规则。很多人就是卡在“本地能访问,服务器访问不了”这个环节。
5.4 常见启动报错的排查对照表
碰到报错,先看控制台异常栈第一行,那行基本直接告诉你问题在哪。我总结一张对照表,按图索骥能解决大部分问题:
| 报错现象 | 大概率原因 | 处理方式 |
|---|---|---|
| Application run failed | 数据库连接不通 | 检查 MySQL 是否启动、账号密码、库名 |
| Port already in use | 端口被占用 | 换端口或杀进程 |
| 页面能打开但没有列表数据 | 数据库表为空或表名不匹配 | 导入 SQL,检查表名前缀 |
| 登录提示密码错误 | 加密算法不一致 | 确认注册和登录的加密规则统一 |
| 接口返回 404 | 上下文路径或路径前缀问题 | 检查 server.servlet.context-path |
| 静态资源加载不出来 | 拦截器拦截了静态资源 | 放行 static、css、js 路径 |
6. 1万字技术文档怎么写:论文结构和代码的对应关系
6.1 论文的每一章,都能在代码里找到对应
标题里提到的“论文文档 1 万字以上”其实是这类项目的标配。很多同学拿到文档模板,不知道该怎么和自己的代码对应起来。其实只要理解了论文结构和代码实现是一一映射的,写起来就会顺畅很多。
常规的毕业论文结构可以这么对应:
- 绪论:写研究背景、选题意义、国内外现状。对应你为什么要做旅游管理系统,可以落在旅游信息化、线上预订体验这些场景上。
- 需求分析:放用例图、功能需求、非功能需求。对着第 1 章的功能清单写,每一条需求都要能在代码里找到实际对应。
- 系统设计:写总体架构图、模块设计、数据库设计。架构对着技术分层画,数据库设计直接放表结构说明和 ER 图。
- 系统实现:放每个模块的界面截图和核心代码。对应 Controller 和 Service 层,代码贴重点,不要把整个类贴进去。
- 系统测试:写测试环境、测试用例、测试结果。Excel 表列清楚每个用例的步骤、预期、实际结果。
- 总结与展望:总结做了哪些工作,后续还可以扩展什么功能。
一个核心原则:代码里实现了什么,论文就写什么;代码里没有的,论文里别硬吹。答辩老师不一定会逐行看代码,但很喜欢问“这个模块在代码里哪个位置”。答不上来比论文写得平庸严重得多。
6.2 截图、测试用例和答辩前的资料准备
论文里的系统截图质量比大多数人想象的重要。截图之前一定要把界面整理干净,测试数据不要乱填。比如演示添加景点的功能时,景点名称、城市、描述都要写正式一些,一张乱七八糟的截图会让整篇论文的专业度大打折扣。
测试用例表建议按这个格式准备:
| 用例编号 | 测试项 | 操作步骤 | 预期结果 | 实际结果 | 结论 |
|---|---|---|---|---|---|
| TC-01 | 用户登录 | 输入正确用户名密码点击登录 | 登录成功跳转首页 | 登录成功 | 通过 |
| TC-02 | 用户登录 | 输入错误密码点击登录 | 提示密码错误 | 提示密码错误 | 通过 |
| TC-03 | 景点分页查询 | 输入关键字点击搜索 | 返回匹配列表且分页正确 | 符合预期 | 通过 |
| TC-04 | 下单流程 | 选择门票生成订单 | 生成待处理订单 | 下单成功 | 通过 |
| TC-05 | 管理员确认订单 | 后台点击确认 | 状态变为已确认 | 状态更新 | 通过 |
论文格式同样不能输。页边距、字体、目录、页码这些地方,提交前认真检查一遍。答辩时还可以把第 5 章提到的技术细节准备成自己的亮点:JWT 无状态鉴权解决分布式场景下的扩展问题、MyBatis Plus 提升单表 CRUD 效率、乐观锁避免库存超卖。这些不算重大创新,但每一句都能说明你真的动了脑子,而不是纯模板搬运。
6.3 技术文档里更适合展开写什么
如果你拿到的源码包里已经有 1 万字以上的文档,别直接原样交上去,一定要自己重新捋一遍。最值得展开写的地方,恰恰是那些你在调试过程中真正处理过的问题。
比如数据库时区问题,文档里只写“连接配置见 application.yml”,但你在第 5 章的排查记录里可以清晰描述:没加 serverTimezone 时启动报了什么错,为什么会有时区概念,加上 Asia/Shanghai 后问题怎么消失。这种真实场景描述,比任何抽象原理都更适合放进“关键技术问题解决”小节。
再比如订单状态流转,文档可以不止写“订单有四种状态”,而是写清楚:为什么不用乐观锁之外的做法,status 字段的取值范围如何约定,管理员确认与用户取消同时发生时系统如何处理。这就是从“能跑”到“懂原理”的区别,也是论文写得有深度的关键。
我自己实际操作中最大的体会是:这类系统的代码量其实不算大,真正拉开差距的往往是对业务逻辑的完整理解、对调试部署过程的复盘、以及文档和代码的一致性。把这些事情做扎实,比多写一万行花架子代码有意义得多。如果你正在折腾这类 Springboot 旅游管理系统项目,建议先把数据库脚本导入跑通,再逐个接口跟着看一遍,最后再把部署这关完整走一遍,你会发现它其实没有想象中那么复杂。