拓冰建站拓冰建站
首页 / 资讯中心 / 正文

球馆预约系统实战:Spring Boot+Redis+乐观锁全流程设计

1. 项目概述这门课设到底做了什么先说说这个项目在我眼里的定位。新起点球馆预约系统说白了就是一个带后台管理的线上场地预定平台核心解决两个问题一是让用户不用到店、不用打电话在手机上就能看场地空闲状态、选时段、下单预约二是让球馆管理员能实时掌握场地使用情况、订单数据、营收流水把过去靠Excel和微信群接龙的运营方式彻底搬到线上来。如果你正在纠结毕设选题我强烈建议你做这类“预约运营管控”双端系统。原因很直接这类题目具备完整的业务闭环有用户端、有管理端、有订单流、有数据统计用来展示你的工程能力、数据库设计能力和业务思考能力都非常合适。而且球场预约天然有时段、场地、价格、状态这些概念能做的事非常多不怕没东西写也不怕做出来像“玩具项目”。从技术实现角度来看这个系统用Java Spring Boot作为后端主框架数据持久层用MyBatis-Plus数据库选MySQL前端可以用Vue或者服务端模板引擎Thymeleaf再用Redis做热点数据缓存和分布式锁。整套技术栈属于当前中小型管理系统的主流配置不管是找工作还是面试聊项目都有东西可以讲。本篇文章我会把这个系统的设计和实现完整拆开讲包括数据库表怎么建、核心预约流程怎么防冲突、状态机怎么设计、运营统计怎么做、上线部署踩过哪些坑以及答辩时老师大概率会问什么。老实说球馆预约系统的难点不在CRUD而在业务规则的严谨性和并发场景下的数据一致性这两块我会重点展开。2. 整体设计思路与方案选型2.1 为什么选Spring Boot而不是其他框架做毕设选框架的时候我见过不少同学在Spring Boot、SSM、甚至Servlet原生开发之间犹豫。我的建议是直接Spring Boot不需要纠结。Spring Boot最大的优势是“约定大于配置”你不需要像SSM那样写大量的XML配置文件。一个起步依赖加几个注解就能把项目跑起来。这对毕设来说非常重要因为毕设的核心精力应该放在业务逻辑上而不是花两周时间调配置。另外一个隐形的好处是面试聊项目时Spring Boot本身就是高频话题你做了这个项目顺带就把自动配置、启动流程、 starter机制这些面试点都覆盖了。Spring Boot还有一个经常被人忽略的好处就是配套生态成熟。集成MyBatis-Plus有现成的starter集成Redis有现成的starter做接口文档有Knife4j做权限校验有Sa-Token或者Spring Security几乎你想要的功能都有现成的轮子而且中文文档和踩坑帖子非常多遇到问题搜一下基本都有答案。这对毕设周期紧张的同学来说价值非常大。2.2 系统整体架构四层架构的概念不是背出来的Spring Boot项目通常遵循四层架构也就是Controller层、Service层、Mapper层、Entity层再加上DTO/VO这些传输对象。很多同学背“四层架构”这个词很熟但真到自己写代码的时候所有逻辑全糊在Controller里这其实是不对的。我的习惯是Controller只做参数接收和结果封装不写任何业务逻辑Service层承载全部业务规则比如校验场地是否可约、计算订单金额、更新场地状态Mapper层只负责数据库交互复杂的SQL写注解或XMLEntity对应数据库表结构DTO/VO负责接口出入参。这样做的好处往下做就知道了。比如后期要给系统加一个“会员折扣”功能如果你把逻辑都写在Controller里改起来要动接口层一不小心就把对外参数结构改了但放在Service里你只需要加一个折扣计算的方法管它前端怎么传参内部逻辑变了对接口无感。这个优势在答辩演示完完整功能后老师如果追问“你系统的扩展性体现在哪里”你就能用这种分层设计来讲有实际案例背书比背概念有说服力得多。2.3 技术栈选型的细节考量后端基础框架Spring Boot 2.7.x或3.x都可以建议选2.7.x原因是网上资料最多遇到问题最好查。如果学校对版本有要求或者你想用新特性再考虑3.x但要注意3.x基于Jakarta命名空间一些老教程的代码不能直接复制。持久层框架MyBatis-Plus不是纯MyBatis。MyBatis-Plus提供了BaseMapper单表CRUD不用写SQL这对预约这种核心表操作频繁的系统来说开发效率提升很大。复杂查询比如多表关联统计再手写XML。数据库MySQL 8.0InnoDB引擎utf8mb4字符集。这个没有争议属于标准配置。缓存与并发工具Redis用来缓存场地时段数据、存储验证码、实现分布式锁。预约高峰期会出现多个用户抢同一时段的情况用Redis做分布式锁能有效避免超卖。前端两种情况。如果你前端基础一般建议用Thymeleaf模板引擎服务端渲染学习成本低不用管跨域问题如果你前端有一定基础可以用Vue3 Element Plus Axios前后端分离视觉效果更好答辩时加分明显。我自己的项目用的是前后端分离方案下面讲实现时会提到跨域处理和接口联调的一些坑。接口文档Knife4j自动生成在线接口文档调试接口很方便答辩时给老师演示接口文档页面也很加分。3. 数据库设计与核心表结构3.1 设计思路先画ER图还是先建表很多同学做数据库设计习惯直接打开Navicat建表边建边想这样很容易出现字段冗余、表间关系混乱的问题。我的建议是先在纸上画ER图如果你用draw.io这类工具画好放进论文里会更好看梳理清楚实体和关系再动手建表。球馆预约系统核心实体大概是这些用户User、场地Venue、场地时段TimeSlot、预约订单Order、订单状态记录OrderStatusLog、公告Notice、系统管理员Admin。如果要做运营管控还可能需要一个价格策略表PricePolicy或者计费规则表。这些实体之间的关系不复杂用户对订单是一对多场地对时段是一对多用户选择某个场地某个时段生成订单订单表通过外键关联用户和场地同时冗余存储时段信息和价格快照。这里为什么要冗余价格快照因为场地价格后期可能调整如果不做快照订单表里只存场地ID等你要查历史订单金额时价格已经改了数据就对不上了。这个细节很能体现你的设计经验建议论文里写一下。3.2 核心表字段详解用户表sys_userid主键自增即可不需要用雪花ID毕设项目单库单表完全够用username登录用户名唯一索引password密码必须加密存储推荐BCryptnickname昵称phone手机号role角色区分用户和管理员。这个字段看起来很普通但设计上可以多聊一句根本不需要单独建一张角色表因为系统角色只有“用户”和“管理员”两种用int字段标记就行。如果你建角色表、权限表、用户角色关联表反而过度设计答辩时容易被追问细节status账号状态正常/禁用create_time、update_time创建和更新时间所有表都建议加场地表venueid主键name场地名称比如“1号羽毛球场地”type场地类型羽毛球、篮球、乒乓球等location位置描述description场地介绍cover_image场地图片URLprice_per_hour基础价格元/小时open_time、close_time开放时间status场地状态启用/停用/维护中时段表time_slot球场预约是典型的按时间片预约场景时段表的设计直接影响业务复杂度。我的方案是时间片固定化每天按半小时为一个切片从早8点到晚22点一天28个时段。时段表存日期slot_date和开始/结束时间这样用户端展示时按日期场地查时段即可简单直观。id主键venue_id关联场地IDslot_date日期start_time开始时间end_time结束时间is_booked是否已被预约0空闲/1已约/2锁定version乐观锁版本号用于并发控制这个is_booked字段要重点讲一下很多人会把“是否已约”放到订单表里通过查询去判断但这样每次查询都要JOIN订单表性能差不说写起来也麻烦。直接冗余一个字段在时段表里配合下面讲的乐观锁既能保证不超卖查询时又只需要单表查询。订单表reservation_orderid主键order_no订单编号全局唯一。建议格式yyyyMMddHHmmss 4位随机数既好看又好排查问题user_id下单用户IDvenue_id场地IDtime_slot_id时段IDslot_date预约日期冗余存储方便按日期查询start_time、end_time开始和结束时间冗余存储展示列表时不用联表amount订单金额快照pay_status支付状态0待支付/1已支付/2已退款/3未支付取消order_status订单状态0待使用/1已完成/2已取消/3已爽约remark备注create_time、update_time订单状态和支付状态为什么分开设计因为状态是两维的一个订单可能支付了但还没去场地也可能预约了但没支付超时取消。如果合成一个状态字段逻辑会非常绕。分开后订单状态只管使用流程支付状态只管资金流程各司其职。运营统计相关表运营管控涉及报表统计我建议做一个每日统计表daily_statis用定时任务每天凌晨汇总前一天的数据。为什么不用实时统计因为运营数据不要求秒级实时每天汇总一次足够而且历史数据不会变实时查询反而浪费数据库性能。id主键stat_date统计日期total_order总订单数total_amount总营收total_venue_hours场地使用时长occupied_rate场地利用率cancel_order取消订单数no_show_order爽约订单数create_time3.3 数据库设计中的3个坑第一付款金额不要用decimal(10,2)以外的方案。float和double都有精度问题涉及金额一律用decimal。这一点在答辩时几乎必问你主动说出来反而显得细心。第二不要在外键上做物理外键约束。逻辑外键就够用了物理外键在删除和更新时会带来很多麻烦。比如你删一个用户如果他有历史订单物理外键会直接报错你还要先去删订单逻辑外键则可以在应用层做判断灵活性高很多。第三索引不要滥用。我给订单表加了三个索引user_id查我的预约列表、venue_id slot_date查某天某场地的订单、order_status查待处理订单。前面两个高频查询必须加后面的状态索引是用来支撑管理端筛选的。其他字段不加避免索引过多影响写入性能。4. 核心功能模块实现预约流程是真正的重头戏4.1 用户注册登录与权限控制登录这一块看起来是基本功但有个点很多同学会忽视密码不能明文存也不能用MD5存。MD5已经被彩虹表打得千疮百孔了正确的做法是使用BCrypt算法加盐哈希。Spring Security自带BCryptPasswordEncoder如果你不想引入Spring Security其实毕设可以不用太重了直接用jBCrypt库或者Spring的spring-security-crypto模块里的BCrypt类都行。我自己的做法是引入spring-security-crypto依赖只调用BCryptPasswordEncoder的encode和matches方法不引入完整的Security配置避免过滤器链带来的复杂度。登录成功后我用JWT生成token返回给前端前端每次请求在请求头带上token后端通过拦截器校验。这里有个经验JWT的过期时间不要设置太长建议2小时否则安全性差。为了不影响用户体验前端可以在token即将过期时主动调刷新接口这样既安全又无感。另一个细节是拦截器的放行规则一定要配好。/api/user/login、/api/user/register、/api/venue/list、/api/venue/detail这些公开接口直接放行其他接口要校验token。如果不小心把管理端接口也放行了那就是重大安全漏洞。建议写个测试用例专门验证未登录访问受保护接口返回401别偷懒。4.2 场地与时段展示缓存策略用户进入预约页面后系统要展示“某天某场地某时段是否可约”这类查询的特点是读多写少——用户会反复刷新看时段但真正下单的动作相对少很多。所以这类接口必须做缓存。我的做法是用Redis缓存当天和未来七天的场地时段数据key格式为venue:slots:20240615:1日期场地IDvalue是一个包含时段列表的JSON字符串过期时间设置为1小时。用户查询时先走Redis没有缓存再查数据库并回填缓存。这里要注意缓存一致性。用户下单成功后对应的时段状态变了必须把涉及的那条缓存删掉或者更新。我用的是“先更新数据库再删除缓存”策略然后设置Redis过期兜底。这个思路在答辩时可以说清楚为什么这么做因为先删缓存再更新数据库的话如果数据库更新失败缓存中就是旧数据用户看到的还是“可约”点进去却约不了体验非常差反过来先更新库再删缓存即使删除失败缓存里的数据和数据库不一致也只是临时的等缓存过期就自动纠正了。4.3 下单预约流程乐观锁防止超卖这块是整个项目的核心难点。球馆预约是典型的资源抢占场景同一个场地同一时间段同时来了两个用户都要约该怎么保证只有一个人能成功最简单的方案是用数据库的行锁也就是select ... for update但这种方式对数据库连接占用时间太长并发高时会积累大量锁等待。更优雅的方案是乐观锁。我的实现是这样的在time_slot表加一个version字段用户提交预约时SQL语句写成UPDATE time_slot SET is_booked 1, version version 1 WHERE id #{timeSlotId} AND is_booked 0 AND version #{version}执行这条SQL后如果返回的影响行数为1说明更新成功这个时段被我锁定了继续创建订单如果影响行数为0说明有别的用户先下单了这个时段已经被约走直接给当前用户返回“该时段已被预约”的提示。这里还有一层保障下单操作本身要放在事务里。Service层方法加Transactional注解先锁定时段再插入订单任何一个步骤失败就整体回滚。这两种机制加在一起就能保证不会出现两个用户都看到“可约”结果双双下单成功的情况。有一个细节要特别提醒乐观锁不是万能的。如果系统并发量真的很大比如上万用户同时抢一个热门时段乐观锁会导致大量请求打到数据库上然后失败数据库压力会非常大。针对这个场景更优的方案是用Redis分布式锁先让请求在Redis层面排队只有拿到锁的请求才进入下一步。毕设系统到不了这个量级但我在设计时也预留了分布式锁的实现方案下面会讲到。4.4 Redis分布式锁的引入进阶方案既然标题里有“运营全流程管控”光靠乐观锁其实不够炫。我在下单接口上额外加了一层Redis分布式锁用setnx命令实现String lockKey lock:venue:slot: timeSlotId; boolean locked redisTemplate.opsForValue() .setIfAbsent(lockKey, 1, Duration.ofSeconds(10)); if (!locked) { return Result.error(系统繁忙请稍后重试); } try { // 执行下单核心逻辑 } finally { redisTemplate.delete(lockKey); }等等这里有个坑因为下单逻辑要查数据库、写数据库执行时间可能超过锁的过期时间如果第一个请求还没执行完锁就过期了第二个请求就能拿到锁进来还是会出现并发问题。这个问题怎么解决用Redisson它内部有看门狗机制会主动给锁续期。实现方式RLock lock redissonClient.getLock(lock:venue:slot: timeSlotId); boolean locked lock.tryLock(3, 10, TimeUnit.SECONDS);Redisson的看门狗默认每10秒续期一次只要业务没执行完锁就不会失效这个方案在真实高并发场景里是经过验证的。不过说实话毕设答辩时能把乐观锁讲明白就已经超过一大半人了。分布式锁可以作为“扩展思考”提出来讲清楚它解决的场景和实现原理这会是你的加分项。4.5 订单状态机让状态流转清晰可见预约系统的订单状态不是简单的“有”和“无”而是一套状态流转。我这边的核心状态是待使用已支付→ 已完成待支付 → 已取消待使用没来签到 → 已爽约。在代码层面我建议把状态流转单独封装成一个类或者一组常量比如用枚举OrderStatusEnumpublic enum OrderStatusEnum { TO_BE_USED(0, 待使用), COMPLETED(1, 已完成), CANCELLED(2, 已取消), NO_SHOW(3, 已爽约); private final Integer code; private final String desc; // getter、constructor省略 }为什么要单独写枚举而不是用魔法数字到处硬编码因为状态值在多个地方都要判断支付回调要更新订单状态、定时任务要把超时未支付订单置为取消、场地签到时要把待使用改已完成。如果散落着0、1、2、3这种魔法数字后期改需求的时候排查起来非常痛苦一个数字写错就是线上事故。状态机有一个很重要的规则是不允许非法跳转。比如一个已完成订单不能再次取消一个已取消订单不能又被标记为已使用。我实现了一个简单的状态校验方法public boolean canTransitTo(OrderStatusEnum target) { switch (this) { case TO_BE_USED: return target COMPLETED || target NO_SHOW; case PENDING_PAYMENT: return target CANCELLED || target TO_BE_USED; // ...其他状态 } return false; }在下单、取消、签到这些操作里先调用这个方法校验不通过直接抛异常。这样一个逻辑基本堵死了所有业务流异常的可能性。这里我还记录了订单状态变更日志表谁在什么时间把订单从什么状态改成了什么状态都在表里留痕。运营人员查客诉纠纷时这个日志就派上用场了。这也是“运营全流程管控”的一个重要体现。4.6 支付流程对接支付宝沙箱环境支付功能是计费系统里很出彩的一环很多同学不敢做怕麻烦。其实做起来一点也不复杂推荐对接支付宝沙箱环境沙箱sanbox environment完全模拟真实支付流程但不需要真实资金。支付宝官方文档写得还算清楚核心步骤就是后端生成订单时调用支付宝SDK的alipay.trade.page.pay接口拿到一个支付表单字符串返回给前端前端把这个表单渲染成页面用户扫码或输入账号支付支付宝异步通知后端支付结果后端验证签名后更新订单状态。这里我有几个经验之谈一是支付宝回调地址notify_url必须是外网可访问的地址。你本地localhost是收不到回调的所以要么部署到公网服务器上要么用内网穿透工具把localhost映射到公网。答辩前一定要反复测试这个环节。二是异步通知的验签绝对不能省。支付宝说回调就真的是支付宝发来的但你要验证签名、验证app_id、验证订单金额防止别人伪造回调。你想想如果你只判断一个“支付成功”的参数别人也可以POST给你你的系统就白给了。三是回调处理要幂等。支付宝异步通知可能发送多次你的业务必须保证重复通知不会重复更新订单状态。最稳妥的办法是在更新订单时加条件WHERE id #{id} AND pay_status 0如果影响行数为0说明已经处理过直接返回success给支付宝。4.7 管理端的运营管控功能管理端是“运营全流程管控系统”的落地载体核心功能包括场地管理、订单管理、用户管理、数据统计、公告管理等。场地管理除了最基本的增删改查还有一个比较实用的功能是“一键设置停用”。比如某块场地临时维护管理员停用后用户端对应场地立即不可约。这个功能的实现难度不大就是在venue表加一个status字段查询场地列表的时候过滤掉status0的场地。订单管理管理端要能按日期、场地、用户、订单状态多条件筛选订单还要能手动取消异常订单。这里要注意一个问题分页查询性能。如果数据量大JOIN用户表、场地表再排序会越来越慢所以冗余字段就派上用场了——直接按订单表的冗余字段筛选和排序不走JOIN。数据统计这是运营管控的核心。我实现了一个统计面板展示当日订单量、营收、用户增长数、场地利用率等核心指标同时展示近7天趋势图。图表用ECharts绘制后端接口返回格式化的统计数据前端直接渲染。还有一个比较能提现业务思考的统计是“场地热门时段分析”统计每个场地哪些时段预约最多这个数据可以直接指导球馆定价——热门时段上浮价格冷门时段做促销。定时任务我用Spring自带的Scheduled注解实现了几个定时任务每天凌晨统计前一天数据并写入日报表每5分钟扫描一次超时未支付订单下单超过15分钟未支付自动取消并释放时段每天早上自动将当天所有未签到的待使用订单标记为爽约并释放场地。关于定时任务我想多说一句Spring的Scheduled默认是单线程串行执行的如果有多个任务必须保证任务之间没有耗时的IO操作冲突。我所有定时任务内部的逻辑都比较简单就是一次查询加批量更新所以单线程完全够用。如果将来任务多了再考虑集成XXL-JOB分布式任务调度平台这个作为扩展点在论文的展望部分提一下倒是很加分。5. 前端设计与接口联调演示效果不能拉胯5.1 用户端页面设计毕设答辩时老师是能看到你系统的实际页面的所以前端的美观程度直接影响印象分。不需要多炫酷但一定要干净、统一、有层次感。用户端我设计的页面包括首页场地展示公告轮播、场地列表页按类型筛选、场地详情页展示场地照片、介绍、价格、可选时段、预约确认页选择日期、场地、时段确认金额、订单列表页待使用/已完成/已取消分类、个人中心页。这里有一个交互细节场地详情页的时段选择器我用的是横向滑动的日期选择条今天/明天/后天……加每个日期的时段网格。用户先选日期再选时段时段网格中灰色代表不可约、绿色代表可约、蓝色代表自己已选。选中时段后底部弹出当前选择信息和“立即预约”按钮。这个交互模式参考了电影票选座的设计操作流程清晰几乎没有学习成本。5.2 管理端页面设计管理端我用了独立的布局左侧菜单栏右侧内容区。菜单按功能模块划分仪表盘数据统计、场地管理、订单管理、用户管理、时段管理、公告管理、系统设置管理员密码修改等。管理端的表格统一用Element Plus的el-table带分页、筛选、排序功能。场地管理页支持上传场地图片文件上传接口用MinIO或者本地存储都行考虑到部署简单我用的是本地存储上传的文件存到服务器某个目录下数据库存访问URL。实际操作中管理端最需要注意的是权限控制。普通用户如果直接访问管理端的API地址能不能拿到管理数据在接口层面必须做双重拦截一是登录拦截没登录不能访问二是角色拦截非管理员角色不能访问管理接口。我用拦截器实现检查请求路径前缀和请求头里的token信息再校验当前用户的role是否为1。5.3 前后端分离下的联调经验我选的方案是前后端分离前端用Vue3后端用Spring Boot两者独立部署通过HTTP接口通信。联调时遇到最多的就是跨域问题。前端访问后端接口浏览器跨域报错是家常便饭。我的解决方案是写一个CORS配置类在Spring Boot中注册一个WebMvcConfigurer配置允许的跨域来源。开发环境允许本地前端地址跨域生产环境则要指定域名或IP。配置好后前后端就能正常联调了。联调过程中我推荐用Knife4j生成接口文档让前端人员能边看文档边对接。每个接口要写清楚请求方式、请求参数、返回值结构、错误码含义。这一点做好了前后端联调能少吵十次架。另外有个很实用的经验接口返回值必须统一格式。我定义了一个统一响应类Result包含code、message、data三个字段code为200表示成功其他表示各种业务错误。前端所有请求都先检查code再处理数据避免出现有的接口返回{success: true}、有的返回{status: 0}这种混乱局面。6. 常见问题与排查技巧实录6.1 并发预约导致超卖如何排查这个问题我在测试阶段真实遇到过。当时用JMeter模拟100个并发请求预约同一个时段的场地结果有3个请求返回了“预约成功”。这说明乐观锁没有生效。排查后发现原因很简单我的time_slot表里的is_booked和version字段在MyBatis-Plus的实体类里没有加乐观锁插件注解。光在数据库层面UPDATE语句里写了version条件还不够MyBatis-Plus有一个Version注解并且需要在配置类里注册乐观锁插件UPDATE时它会自动在SQL后面拼接AND version #{version}。修复方式// 实体类字段上添加 Version private Integer version;// 配置类注册乐观锁插件 Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; }修复后重新压测100个并发请求只有1个成功其余全部返回“该时段已被预约”问题解决。6.2 支付宝回调收不到怎么排查这个问题我调试了好几个小时其实原因特别基础阿里云的服务器安全组没有开放对应端口或者回调URL配置的端口不对。具体排查步骤是确认沙箱环境的回调地址是否配置正确必须和外网映射地址完全一致在服务器上用curl模拟POST请求打一下本地接口确认服务本身能收到请求查看Spring Boot日志确认异步通知是否到达了Controller层如果是用内网穿透工具确认免费域名没有过期或者流量限制6.3 定时任务没有执行怎么排查有个同学问过我他的Scheduled定时任务不执行检查了注解、代码都没问题。后来才发现启动类Application上漏了EnableScheduling注解。这个问题真的很容易漏。Scheduled只声明“这个方法是一个定时任务”EnableScheduling才是“开启定时任务的开关”两个缺一不可。6.4 前端跨域报错CORS怎么解决跨域问题在前后端分离项目中一定会遇到解决方案我在上文提过在Spring Boot里配置CorsFilterBean public CorsFilter corsFilter() { CorsConfiguration config new CorsConfiguration(); config.addAllowedOrigin(http://localhost:5173); // 前端地址 config.addAllowedMethod(*); config.addAllowedHeader(*); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, config); return new CorsFilter(source); }这里有一个注意点allowCredentials(true)和addAllowedOrigin(*)不能同时使用。如果需要携带Cookie就必须指定具体的域名不能用通配符。这个坑很隐蔽报错信息也不直观容易被卡住。6.5 常见问题速查表问题现象可能原因解决方案订单状态一直不变支付宝回调没配置或端口不通检查回调地址、开放对应端口并发抢同一时段都成功乐观锁未生效检查Version注解和乐观锁插件配置定时任务不执行启动类缺少EnableScheduling补充开启调度注解前端跨域报错CORS未配置或配置错误添加并正确配置CorsFilter管理接口能被普通用户访问缺少角色权限拦截登录拦截器角色校验双重判断Redis缓存数据一直是旧值更新数据库后未删除缓存按“先更新库再删缓存”策略处理7. 部署上线与答辩准备7.1 本地部署还是云服务器部署毕设答辩的演示环节如果用localhost演示一旦教室网络环境不稳定可能会很尴尬。我强烈建议买一台轻量云服务器把系统部署上去答辩时直接在公网域名/IP上演示效果和稳定性完全不一样。部署步骤大概是服务器装JDK 8或11、MySQL、Redis、Nginx把Spring Boot项目打成jar包用java -jar启动前端打包成dist目录交给Nginx托管在Nginx配置反向代理把/api前缀的请求转发到后端端口。这里提一个部署时的踩坑经验MySQL连接地址不要用localhost因为jar包运行时localhost指向的是本地环境如果你在云服务器上部署要改成云服务器的内网IP或者127.0.0.1。更安全的做法是在application.yml里把数据库、Redis的连接信息用环境变量方式配置不要写死在代码里。7.2 答辩时老师爱问什么根据我自己的答辩经历和别人遇到的情况老师围绕这个项目主要会问几个方向的问题为什么选这个课题往“体育场馆信息化转型的痛点”上靠说明传统电话预约、纸质登记存在信息不透明、效率低、无法统计运营数据的问题系统化解决这些问题。项目有哪些难点你怎么解决的并发预约超卖问题乐观锁事务、支付异步通知的幂等处理条件更新订单、缓存和数据库一致性先更新库再删缓存、运营数据统计怎么高效生成定时任务预聚合。系统的安全性怎么考虑密码BCrypt加密、JWT身份认证、接口拦截鉴权、支付宝回调验签、SQL注入用预编译机制防住。这些点被问到的概率极高提前把答案组织好。系统的可扩展性目前单体架构可支撑中小球馆规模后续如果多门店可以拆成微服务引入消息队列异步处理订单通知引入分布式任务调度平台管理定时任务。这些方向提一嘴说明你有思考。数据库设计为什么这么设计重点讲时段表冗余is_booked字段的意义、订单表冗余价格快照的意义、为什么要用逻辑外键不用物理外键。7.3 论文撰写的建议最后简单说说论文。球馆预约系统这类项目写论文结构大概是绪论研究背景、国内外现状、研究内容、相关技术介绍Spring Boot、MyBatis-Plus、Redis等、系统分析可行性分析、需求分析、系统设计总体架构、功能模块设计、数据库设计、系统实现各模块核心功能展示代码和截图、系统测试功能测试、性能测试、总结与展望。写论文最忌讳的是“贴大段代码”。代码只是实现细节老师更关心的是你的设计思路和问题解决过程。每个功能模块建议围绕“业务背景 → 设计思路 → 实现要点 → 效果截图”这个结构写重点写清楚关键逻辑的实现思路比如状态机怎么流转、并发问题怎么解决这些才是论文的亮点。8. 项目扩展方向与个人心得项目做完之后不要急着封板看看还有哪些方向可以扩展。对我来说这个球馆预约系统其实只完成了最基本的预约管理闭环真要落地商用还能做很多事会员体系与充值卡预充值钱包、会员折扣、积分兑换这些能有效提升用户粘性也丰富了运营手段。教练预约与陪练服务除了场地还能约教练涉及教练排班、课程包、服务评价业务模型会复杂很多但价值也直线上升。多门店支持如果球馆开了分店场地、管理员、运营数据都要按门店维度隔离这时系统架构就要重新设计了。营销工具优惠券发放、拼团预约、限时秒杀这些活动本质也是并发场景可以和已有的分布式锁方案联动。消息通知预约成功、提醒到场、爽约提醒通过短信或微信公众号模板消息触达用户提升服务体验。就我个人体会而言做完这个项目最大的收获不是会写CRUD而是建立起了一套“从需求到设计到实现到测试部署”的完整工程化思维。你会发现很多坑其实都可以在设计阶段提前规避。比如一开始就把订单状态做成枚举、把金额用decimal、把并发控制方案提前想好后面写代码时就会顺畅很多。最后再分享一个小技巧项目命名建议正规一点包名用com.xinqidian.venue这种结构代码注释写清楚关键逻辑接口命名遵循RESTful规范。这些细节平时觉得无所谓但答辩时老师翻你代码看到的是一份整洁规范的项目印象分会好很多。关于系统设计或者Spring Boot实践方面想继续交流的同学也欢迎随时沟通。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门