Spring Boot智慧旅游系统实战:从支付闭环到并发库存优化
之所以在2026年看到基于Spring Boot的智慧旅游系统这类课题还愿意接说明你清楚一个事实技术本身不新但落地的完整度依然是大多数毕设和商业项目之间最大的分水岭。这个课题涵盖了微服务入门、数据可视化、消息队列、缓存策略、权限控制、地图API集成等一整条Web开发链路对即将毕业的同学来说是最稳妥的选题对想转行Java开发的人来说它也是简历上最容易展示综合能力的一类项目。这里的智慧不是加几个AI接口就算数而是要真正打通“游客出发前规划—游玩中导览—游玩后分享”这条完整业务闭环。这篇文章我会从需求拆解、技术选型、数据库设计到前后端联调、并发安全、部署上线都过一遍重点讲清楚每一步的为什么和常见的坑而不是只堆一个能跑的Demo。1. 系统整体设计与需求拆解1.1 课题目标与我为什么建议选择单体模块化拆分拿到这个课题后不要着急写代码。先想清楚一件事你做的系统到底要给谁用、解决什么问题。很多同学一上来就设计十几个表、五六种角色最后自己维护不过来答辩的时候也被问得漏洞百出。我的建议是面向游客和景区管理员两个核心角色做一套线上线下一体化的旅游服务系统。游客端覆盖景区信息查询、门票预订、在线支付、路线推荐、订单管理、游客评论管理端覆盖景区资源维护、门票库存管理、订单审核与退款、用户管理、数据统计看板。这个范围既能体现业务完整性也不会因为追求大而全把自己拖垮。至于架构选型Spring Boot MyBatis-Plus 的经典单体结构足够支撑这个课题展现全部技术点。你可能觉得微服务更炫但毕设答辩的核心不是看你能拆多少个服务而是看你能不能把某个模块的完整链路讲深讲透。单体架构下做模块化拆分——按业务边界分成 system、travel、order、user、statistics 等模块后面如果想演进成微服务也只需要按模块边界拆分不会推倒重来。1.2 角色权限与核心业务流的梳理用户侧的角色我认为需要拆成四类未登录游客、普通注册用户、景区运营人员、系统管理员。未登录游客只能浏览景区详情和公开的旅游攻略一旦涉及预订、支付或者写评论就必须登录这个小的权限边界设计帮你天然规避了大量需要处理游客临时数据的业务复杂度。核心业务流可以抽象成三条主链路第一条是查景区——看详情——下单——支付——生成订单——发布评论的完整消费链路。这条链路一定要走到支付环节哪怕接入的是支付宝沙箱或者微信支付沙箱也必须把支付回调、订单状态修改、库存扣减放到同一个事务闭环里。这样答辩时能直接拿支付掉单超时关单这类高并发下的经典问题来展示你的思考深度。第二条是景区管理——门票库存调整——订单核销——数据统计的运营链路。管理端要能实时看到每个景区的售出率、销售额、游客来源地分布这些你后面做ECharts可视化大屏时直接复用同一套接口。第三条是用户行为采集——热门景区推荐——个性化路线生成。这条链路很多人会做成摆设我建议你哪怕只用简单的标签匹配算法也要把推荐结果真实展现在首页这比在旁边放一个光鲜的AI概念要扎实得多。1.3 为什么单机部署的Spring Boot依然是这类课题最优解有一类误区是既然课题叫智慧旅游就必须上微服务、上消息队列、上分库分表。实际上对于毕设课题来说技术选型要和业务体量匹配。一个普通景区单日游客峰值也就两三万这在单台8G内存的服务器上用Spring Boot配合Redis缓存完全能扛下来硬上微服务带来的服务发现、分布式事务、链路追踪问题反而会把核心业务淹没在环境配置里。真正应该花精力去打磨的是能不能写出性能合格的SQL、能不能把接口响应时间控制在200毫秒以内、能不能在订单提交时通过RedisLua脚本解决库存超卖问题。这些问题才是面试官会追着问的。单体架构下做垂直审视比分布式架构下做水平堆砌更能体现一个开发者的技术判断力。2. 技术选型与核心原理分析2.1 Spring Boot家族的组件搭配逻辑Spring Boot 2.7.x 是截至2026年初依然维护最稳定的版本线JDK 用 1.8 或 11 都行。如果是新项目直接用 Spring Boot 2.7.18 JDK 11 的组合遇到问题能找到的社区答案最丰富不容易踩版本坑。核心组件搭配是这样的Spring MVC处理RESTful接口MyBatis-Plus负责持久层CRUDSpring Security JWT做无状态认证Redis做缓存和分布式锁MySQL 8.0存业务数据。这里面储物不足的是Spring Security的配置复杂度很多人会被过滤器链绕晕。我建议你换个思路不用Spring Security的繁琐配置自己写一个基于JWT的拦截器 自定义注解(RequireLogin、RequireAdmin)来做权限控制。这并不是偷懒因为你会在这个过程中彻底搞懂过滤器、拦截器、HandlerInterceptor和参数解析器的工作机制而且讲真毕设项目里自定义拦截器方案比Spring Security的默认配置更灵活写进简历也能讲得更清楚。2.2 ORM框架MyBatis-Plus相对JPA更适合报表统计如果你对SQL比较熟悉就选MyBatis-Plus如果你更习惯面向对象思维那Spring Data JPA上限更高。但放到智慧旅游这个场景里我明确推荐MyBatis-Plus。原因很简单旅游系统里有大量多表联查和统计报表需求比如查询每个景区本月每日的售票趋势这种SQL用JPQL写起来很难受但用MyBatis-Plus的LambdaQueryWrapper配合自定义XML里的复杂SQL状态管理非常清晰。而且MyBatis-Plus内置的分页插件性能比JPA的分页更可控处理百万级订单表时优势明显。这里给个小技巧不要在主业务表上直接做聚合统计单独建一张stat_daily_report汇总表每天晚上通过定时任务跑批把前一天的订单汇总进去。查询当日数据时直接查这张汇总表页面加载速度能提升一个数量级。2.3 缓存设计Redis解决哪些问题、不解决哪些问题Spring Boot默认支持ConcurrentMapCache这只适合单体调试生产环境要用Redis。Redis在智慧旅游系统里承担三件事缓存不常变化的景区基础信息、缓存用户登录token、承担库存扣减的原子操作。我的习惯是给缓存key设置统一前缀和过期时间比如travel:scenic:{id}景区详情缓存30分钟订单热点数据缓存5分钟token缓存2小时。还有一个谁做谁爽的小设计——用Spring Cache的Cacheable注解配合自定义KeyGenerator可以统一生成前缀避免团队里key格式五花八门。特别注意Redis不解决缓存与DB的一致性难题只能缓解。我的方案是先更新数据库再删除缓存。主动删除失败就靠TTL兜底保证最终一致性。系统里评论数、浏览量这类高频更新字段不要每次1都命中DB先放Redis做原子自增定时任务每5分钟同步一次数据库。2.4 MySQL表结构与索引设计的核心思想数据库表设计是答辩高频区。智慧旅游系统至少需要这些表用户表、景区分类表、景区信息表、景区图片表、门票SKU表、订单表、订单明细表、支付流水表、评论表、收藏表、用户浏览记录表、统计日汇总表。表关系尽量控制在两层以内避免过度抽象。底色策略是景区信息和门票SKU分开存。同一景区可能同时存在成人票、儿童票、学生票等多种票价把不同价格的票单独拆成SKU表后续做库存和价格管理会灵活很多。索引设计有一点务必注意订单表中user_id和status要建联合索引查询用户历史订单就是高频SQL支付流水表里order_no如果是按雪花算法生成的直接建唯一索引评论表关联景区ID和时间字段也要建联合索引。别信“索引建越多越好”这种话索引是占空间拖写入的一般单表索引控制在五个以内。3. 数据库设计与核心功能落地3.1 核心业务表结构的具体设计表结构我直接给一个能用的方案字段说明也标注清楚。用户表核心字段id、username、passwordbcrypt加密存储、nickname、avatar、phone、email、role用字符串存USER或ADMIN别用int当枚举答辩时解释起来麻烦、status0禁用 1正常、create_time。景区表scenic_spotid、name、summary概述、detail富文本、category_id、province、city、address、longitude、latitude、cover_image、open_time、closed_time、traffic_info交通指南、view_count、status。门票SKU表ticket_skuid、scenic_id、ticket_name、price用decimal(10,2)、original_price、stock、sold_count、valid_date_start、valid_date_end、refund_policy。订单表ordersid、order_no20位左右雪花ID对外展示、user_id、scenic_id、sku_id、quantity、total_amount、status0待支付 1已支付 2已核销 3已退款 4已取消、pay_type、pay_time、contact_name、contact_phone、visit_date、expire_at、create_time。评论表commentid、order_id、user_id、scenic_id、content、rating1-5分、imagesJSON字符串存最多9张图、reply_content、create_time。浏览记录表user_browse_historyid、user_id、scenic_id、browse_time。这表会越来越大建议定期归档删除90天前的数据。每张表都要有create_time用datetime类型。id统一用bigint自增还是雪花ID订单表用雪花算法其余表自增就行。分页查询时大数据量下的深分页问题要注意后面问题排查章节我会讲。3.2 接口层的RESTful规范与统一返回体设计接口设计看起来是小事但直接决定前后端联调效率。统一返回体我习惯用ResultT泛型包含code、message、data。成功code为200业务失败code为400xx未登录401无权限403服务器异常500。分页返回体单独做PageResultT包含records、total、current、size。接口URL风格统一用复数名词/api/v1/scenicsGET列表、/api/v1/scenics/{id}GET详情、/api/v1/ordersPOST下单、/api/v1/orders/{id}GET详情。所有接口统一加/api前缀方便后面统一做网关路由或者Nginx反向代理。这里有个典型的返回体设计坑不要把数据库字段直接暴露给前端。比如orders表里的status字段前端期望知道的是待支付已支付这类文字状态不是0、1、2的数字。我建议返回给前端时统一转换成VO对象比如OrderVO里放status_desc字段由后端把数字转换成中文。虽然麻烦一点但省去前端自己翻译的破事也显得你后端设计更规范。3.3 在线预订与支付状态机的完整设计这是整个系统的技术重点。从用户提交订单到订单完结中间一定要有清晰的订单状态流转。订单状态我设计了五种待支付(0)、已支付(1)、已核销(2)、已退款(3)、已取消(4)。流转关系待支付可以取消或者支付后变成已支付已支付可以申请退款变已退款或者核销后变成已核销。禁止从已支付直接跳待支付这类非法流转在后端要做状态校验不能只靠前端隐藏按钮。下单链路这样做用户提交下单请求后后端先生成订单记录状态为待支付扣减Redis中的库存用Lua脚本保证原子性接着调用支付宝沙箱或微信支付沙箱生成预支付单把支付二维码返回前端。用户扫码支付成功后支付平台异步回调后端通知接口。后端收到回调后校验签名和金额再更新订单状态为已支付同时累加SKU的已售数量。这里最关键的保证是支付回调处理接口必须对请求参数做验签且必须做到幂等——同一条支付通知可能因为网络原因多次送达后端要做状态判断只有当前状态是待支付的订单才更新为已支付。超时关单的逻辑可以这样实现订单创建时设置过期时间比如10分钟用定时任务每分钟扫描一次所有待支付且过期时间小于当前时间的订单批量取消。不用RabbitMQ延迟队列是为了减少项目部署时的额外依赖。答辩时如果被问到为什么不用延迟消息你可以答延迟队列确实更优雅但考虑到部署成本和实际请求量定时扫描已经满足需求这也是一种合理的技术取舍。3.4 管理端数据统计与可视化大屏的实现要点管理端要有一个带数据可视化大屏的首页。我用ECharts来做柱状图、折线图、饼图数据源就用前面提到的stat_daily_report汇总表。大屏左侧展示景区门票销售额Top10用横向柱状图中间展示近30天每日订单量和销售额趋势用双Y轴折线图右侧展示游客来源地分布用饼图底部是实时订单滚动列表用WebSocket模拟推送。ECharts图表的数据接口要单独写不要和业务接口混在一起。后端返回的格式要迎合ECharts的要求比如饼图需要[{name: 北京, value: 1024}, {name: 上海, value: 2048}]这样的结构你在后端组装好再返回前端拿数据直接塞进option就能渲染。可视化大屏的坑非常隐蔽页面首次加载时全部图表同时请求接口会把后端打懵。建议做一个队列等页面所有DOM节点就绪后再逐个加载图表或者给每个图表接口加一个20~50ms的随机延迟错峰请求。4. 项目实操过程与核心环节实现4.1 IDE选型与项目初始化配置工具建议IntelliJ IDEA社区版够用旗舰版更好、Navicat数据库管理、Postman或Apifox接口调试、Git版本管理。我这里默认你会用Maven做依赖管理不会的话先花半小时学一下Maven的依赖坐标、仓库和生命周期。初始化Spring Boot项目时Spring Initializr上勾选这些依赖Spring Web、MySQL Driver、Lombok、Validation。MyBatis-Plus、JWT、Redis这些依赖手动加进pom.xml版本号用Maven BOM统一管理。建议创建项目时就建好application-dev.yml和application-prod.yml两个环境配置文件。application-dev.yml里数据源、Redis、日志级别、JWT密钥这些全部配置好。重点提一下日志配置生产环境别用默认的Spring Boot日志格式配上logback-spring.xml按天滚动保留30天日志级别INFO。错误日志单独输出到一个error.log文件。这样系统运行出问题排查效率高得多。4.2 我的本地开发环境描述一套省心的本地环境是高效开发的起点。我自己现在的配置是Windows 11 JDK 11 Maven 3.8.x MySQL 8.0 Redis 6.2。如果你的电脑配置有限关掉部分IDE插件也能跑得很流畅。MySQL装好后记得建库设字符集为utf8mb4排序规则utf8mb4_general_ci即可。数据库连接串上加useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai时区必须设置否则日期查询会有8小时偏移。初始化数据不要只用假数据。部分基础数据可以从无版权景区信息源导入但要注意把导入的图片转存到本地静态资源目录避免外链图片挂掉。每个景区配5~8张图片覆盖不同角度和季节这样前端页面展示效果完全不一样。4.3 核心代码实现在线预订的后端链路我挑最核心的在线预订功能把后端链路完整走一遍。用户提交下单请求时Controller接收CreateOrderRequest参数包括scenicId、skuId、quantity、visitDate、contactName、contactPhone。代码分三层Service层方法createOrder()里做这些事校验SKU是否存在且状态为可售校验游玩日期在SKU的有效期内Redis Lua脚本扣减库存这里务必用Lua脚本保证“检查库存充足再扣减”和“扣减”两个动作是原子的插入订单表状态待支付调用支付服务生成支付单返回下单结果和支付二维码链接。public OrderVO createOrder(CreateOrderRequest request, Long userId) { TicketSku sku skuService.getById(request.getSkuId()); if (sku null || !sku.getStatus()) { throw new BizException(该门票已下架); } if (request.getVisitDate().isBefore(LocalDate.now())) { throw new BizException(游玩日期不能早于今天); } // Redis Lua脚本原子扣减库存 boolean success stockService.deductStock(request.getSkuId(), request.getQuantity()); if (!success) { throw new BizException(库存不足); } try { Orders order buildOrder(request, userId, sku); order.setStatus(OrderStatus.WAIT_PAY); ordersMapper.insert(order); // 调用支付网关生成支付单 String payUrl payService.createPrepayOrder(order.getOrderNo(), order.getTotalAmount()); return OrderVO.from(order, payUrl); } catch (Exception e) { // 订单创建失败回补库存 stockService.restoreStock(request.getSkuId(), request.getQuantity()); throw e; } }这代码有两个细节必须掌握第一Redis扣库存成功但数据库插入失败是一定要回补Redis里的库存否则会出现用户看到库存充足但下单失败的隐性Bug第二buildOrder生成订单号时用雪花算法确保全局唯一。4.4 用户端体验优化多维度发现与游玩助手光能下单不够智慧旅游系统的用户端要让游客找得到、看着爽、来得值。我建议实现几个区别于普通CRUD的功能点。第一个是多维度景区搜索用户可以按省份、城市、景区分类、门票价格区间、评分区间组合筛选。这功能听起来简单但做起来有细节比如城市筛选时前端传的是拼音缩写还是城市ID建议统一用城市ID传递拉数据时JOIN城市表。价格筛选用between条件范围内的SKU最低价作为景区的展示价格用MySQL的MIN()聚合配合HAVING过滤。这些SQL都是面试官爱问的高频SQL。第二个是游玩助手功能景区详情页除了基础信息增加一个由Lombok构建的景点游玩时长估算工具条。根据游客选择的不同景点组合动态估算总游玩时间给出推荐行程安排。这个功能可以用简单的贪心算法实现把用户选择的景点按地理位置排序相邻景点之间加上交通时间累加每个景点的建议游玩时长。做出来之后演示效果极强——答辩现场直接演示一键生成我的专属游玩路线比讲十页PPT都管用。第三个是内容互动游客游玩后可以发带图评论管理员可以置顶优质评论。评论列表要支持按热度和时间两种排序方式热度按评论点赞数计算。点赞功能不要单独建一张点赞表然后关联查询直接Redis里的Set结构存储每个评论的点赞用户ID集合页面展示时只查SCARD获取数量就行能省大量查询。4.5 管理端实操流程从上架到退款全流程管理端的核心操作路径管理员登录后先进入景区管理创建景区基础信息上传图片和交通指南再进入门票SKU给景区配置不同票种用户端开始产生订单后管理员在订单管理看到订单列表可以对某笔已支付订单执行退款操作。退款这步要特别注意退款不是简单改订单状态要调用支付平台退款接口退款成功后系统收到异步通知才能把订单状态改为已退款。整个流程要有状态机保护防止重复退款。我在项目里给退款操作加了一个refund_status字段未退款(0)、退款中(1)、退款成功(2)、退款失败(3)。当后台发起退款时立刻把状态改成退款中同时加一把Redis分布式锁防止并发点击多次退款。退款成功回调后更新为退款成功再更新订单主状态。这套状态机保护是我强烈建议你加的体现的是工程意识不是学生思维。4.6 前后端分离部署与跨域问题处理方案智慧旅游系统建议做成前后端分离Vue 3 Element Plus 做后台管理uniapp 或 H5 做用户端如果做App选uniapp能一套代码多端打包。前后端分离下跨域问题必须处理。最稳妥的方案是后端单独写一个CorsConfig配置类允许指定前端域名跨域不需要开发时关闭浏览器安全策略。配置时allowedOriginPatterns用*配合allowCredentials(true)注意不能用allowedOrigins(*)后者和携带Cookie不兼容。生产环境部署时Nginx统一监听着80端口把/api路径反向代理到后端的8080端口把/路径指向前端静态文件目录。这样不存在跨域问题。只有本地开发时需要开启CORS配置。我建议本地开发也使用Nginx做代理转发从开发环境就模拟生产环境减少本地通、线上挂的概率。5. 常见问题与排查技巧实录5.1 分页查询卡死与深分页SQL优化我遇到过最恼火的性能问题之一是后台管理分页翻到第200页时接口响应要5秒以上。这是因为LIMIT 19800, 20是让MySQL扫描前面19800条记录再丢弃掉只返回最后的20条。数据量过万后这种查询极慢。解决方案是用子查询延迟关联先由覆盖索引拿到本次分页需要的20个主键ID再用这些主键ID去主表取整行数据。改写后的SQL大约是SELECT * FROM orders WHERE id IN ( SELECT id FROM orders WHERE user_id 10086 ORDER BY id DESC LIMIT 19800, 20 )这个方法在百万级数据下能把响应时间从5秒压到200毫秒以内。答辩时把这条SQL优化案例主动讲出来面试官通常都是点头的。5.2 高并发下的库存超卖问题与解决方案对比超卖是毕设答辩必问的压轴题。如果你真的遇到了库存剩1件却被两个人同时买走的场景原因一定是扣减库存时没有加原子性保护。解决方案我至少要会三种数据库乐观锁UPDATE stock stock - 1 WHERE stock 0、悲观锁SELECT FOR UPDATE、Redis分布式锁Lua脚本。三种方案我都实测过真实感受是乐观锁在低冲突场景性能最好但高并发下重试次数多体验一般悲观锁实现简单但锁等待会让吞吐量掉得厉害Redis Lua脚本是这套系统的最优解扣减逻辑和查询逻辑在一个脚本里原子执行吞吐量高且不会超卖。Lua脚本长这样local stock redis.call(GET, KEYS[1]) if tonumber(stock) tonumber(ARGV[1]) then redis.call(DECRBY, KEYS[1], ARGV[1]) return 1 end return 0发布一个补充知识点支付的幂等处理讲得通基本上支付链路就能过关。我们的支付回调接口收到支付平台的通知后先查订单当前状态如果已经是已支付直接返回成功并丢弃本次通知这样就不会重复加钱、重复发货。5.3 API接口安全防护与数据校验经验安全是这类系统最容易忽略的地方。最少要做到三件事第一参数校验用Validated注解做强制校验DTO对象里NotNull、NotBlank、Min、Max全部配上。千万别只在写SQL前手动判断代码又臭又容易漏。第二防止SQL注入。MyBatis-Plus的QueryWrapper底层是预编译所以参数不用太担心但写XML自定义SQL时如果用了${}拼接参数先对参数做白名单校验能用#{}绝不用${}。第三用户只能访问自己的资源。比如查看订单详情接口userId不从前端传而是从JWT上下文中获取再校验这个订单确实属于这个用户。防止水平越权漏洞防止用户直接改URL里的订单号去查别人的订单。5.4 环境问题排查时区八小时、Redis连不上、跨域失效这里把我实际遇到的问题列一个速查表现象原因解决办法数据库查询结果比当前时间慢8小时JDBC连接串没设置时区serverTimezoneAsia/ShanghaiRedis报连接超时默认只监听127.0.0.1修改bind 0.0.0.0设置protected-mode no或配置好密码前端跨域报错Nginx未配置或CORS类没加按5.6节方式排查最关键是allowedOriginPatterns别写成allowedOrigins部署后上传图片无法访问图片存到了后端/tmp目录修改spring.servlet.multipart.location为固定目录用Nginx映射本地磁盘路径本地能跑、线上打jar包后报找不到静态资源文件和jar包分离不规范静态资源和模板路径不使用classpath:/配置外部文件路径映射跨域这个问题很奇怪有时开发环境配好了部署到服务器后又失效。原因一般是Nginx层做了跨域拦截优先级高于后端CORS配置或者浏览器对预检请求OPTIONS没放行。建议在Nginx配置里统一加上跨域头然后后端不再单独配置CORS。回源请求由Nginx拦截后全部加上Access-Control-Allow-Origin头。5.5 部署落地与答辩演示时的注意点部署阶段的坑我觉得讲三天三夜也讲不完。最简单的稳妥做法阿里云轻量应用服务器2核4G带宽5Mbps够用 Docker部署 Nginx反代。用docker-compose把MySQL、Redis、后端、前端四个服务编排起来。部署时留三个心眼MySQL的数据目录一定要挂载到宿主机容器重造后数据不丢失用Envoy或conf.d文件把3306、6379端口绑定到127.0.0.1不要直接暴露公网后端容器里用环境变量注入数据库密码别把密码硬编码到yml文件里答辩时万一被问到Docker安全也有话说。答辩演示时最怕当场翻车。我的建议是准备一套完整的演示脚本演示每个功能前先复述我要演示X预期看到Y即使网络出问题也能展示你对业务的掌控力。手机开热点做备用网络本地开发环境再跑一份万一线上挂了立刻切本地。6. 并发与性能测试的基本功6.1 哪些接口一定要做性能测试接口性能要提前心里有数。我的经验是系统里这几个接口必须做压力测试——景区列表页查询接口、景区详情接口、下单接口、支付回调接口。压测工具首选JMeter写个简单线程组就能用。没有JMeter的Postman的Runner也能按固定次数跑并发。更偷懒的用法是直接用Linux下ab命令ab -n 10000 -c 500 -T application/json -p order.json http://localhost:8080/api/v1/orders这个命令意思是模拟500并发总共打1万次下单请求观察TP99响应时间和错误率。以我经验普通服务器上单接口QPS能到500以上TP99小于300毫秒就算可以了。不达标就逐个排查先看有没有慢SQL开启MySQL慢查日志再看Redis缓存命中率再看GC日志。压测能暴露的隐患远比写代码时能预想到的多。6.2 实测中的性能瓶颈定位思路我参与过的一个模拟项目压测时发现下单接口在200并发时就开始大量超时。用Arthas跟踪到热点是stockService.deductStock()里没走Lua脚本而是先查Redis再在Java层判断再扣减这期间有并发窗口导致超卖同时Redis连接数也打满了。优化方案两步走第一扣减逻辑换成Lua脚本单命令原子执行第二Redis连接池最大连接数从8调成50从Jedis换成LettuceLettuce底层是Netty支持多路复用天然比Jedis省连接。改完后压测500并发下单接口TP99稳定在200毫秒内库存也没发生过超卖。这条经验特别适合写进项目的中文技术博客“一次从性能瓶颈到接口优化的实战复盘”。6.3 压测时数据库连接池参数调优Spring Boot默认的HikariCP连接池性能已经很优秀但默认配置并不适合所有场景。压测时数据库连接是最容易先耗尽的那一层。我常用的HikariCP参数maximum-pool-size: 常规项目配20~50压测时可以临时调高到100minimum-idle: 等于maximum-pool-size避免空闲时频繁创建连接connection-timeout: 30000超过30秒拿不到连接直接报错避免长期阻塞。这些参数写死前最好通过压测来确认。你会发现一个规律数据库连接数不是越大越好连接数增大往往意味着数据库线程切换开销变大性能反而下降。压测时看一眼数据库监控指标找到连接数拐点再把参数定下来。7. 从毕设到简历的项目包装建议7.1 项目README与数据库设计文档的整理技巧如果你打算把这个项目放进简历文档整理这步千万别省。项目根目录放一个README.md内容至少包括项目背景与目标、系统架构图用文本或者PlantUML画答辩PPT也能用、技术栈清单、快速启动指南、核心业务流程说明、接口文档链接。接口文档用Apifox或Knife4j自动生成省事又专业。数据库设计文档建议整理成一份database-design.md把每张表的字段、索引逻辑、表关联关系写清楚。面试官问你为什么在这个字段上建索引时你能直接说出判断依据这比写一堆CRUD代码值钱多了。7.2 答辩时如何讲清楚系统的技术亮点答辩现场真正的高光时刻是你能不能把某个模块讲深一个层级。比如讲到Redis库存时不要只说用了Redis做缓存而是说“门票库存这块的并发场景早期用数据库乐观锁解决超卖后来压测发现200并发时性能下降明显最终用Redis Lua脚本替代500并发下QPS突破一千库存准确性也能保证。”这种讲法背后是踩坑的深度装不出来。所以写代码阶段就要有意识地记录这类“从坏到坏、从坏到好”的优化路径。7.3 项目后续升级方向的思考如果学有余力还可以加入这些扩展方向接入基于位置的服务LBS实现“附近热门景点”推荐引入ElasticSearch用搜索中间件替换MySQL的LIKE模糊查询提升搜索体验用消息队列削峰填谷订单创建和生产入库解耦引入对象存储服务图片上传不再落在本地磁盘。不过我的建议是先把核心链路的完整度打磨好再考虑加法。一个能稳定跑通支付、退款、统计、权限的项目已经比大多数只做了CRUD演示的同龄人强很多了。8. 写在最后的实务经验如果你打算在2026年把基于Spring Boot的智慧旅游系统作为毕业设计或者求职项目我的体会是这个课题真正的分水岭不在技术本身而在于你有没有把业务闭环打磨完整。很多同期的人做到能跑就打止了而你要做到好用、讲得清、扛得住追问。多花时间在支付回调和库存一致性上获得的理解深度比多做两个花哨页面有价值得多。最后送一个实用小技巧把前后端代码都传到一个私有仓库里每次上线前打一个tag。遇到线上问题排查时你看着tag版本配合Nginx访问日志和健康检查接口十分钟就能定位是代码问题还是环境问题。这套工作流看起来很简单但很多人就是没养成习惯一次线上事故就能折磨一周。希望这篇文章对你做这个课题有用。所有推荐方案我都实际验证过你照着做至少不会出现大方向上的偏差至于更细节的坑欢迎在评论区留下你的具体报错我看到都会回复。