ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Spring Boot拍卖管理系统毕业设计:从并发控制到全栈实践

Spring Boot拍卖管理系统毕业设计:从并发控制到全栈实践 如果你正在为计算机毕业设计选题看到“基于Spring Boot的拍卖管理系统”这个题目大概率是从学校选题列表里顺手选的——别不好意思承认这个题目的确很“安全”。它不是一个普通的CRUD管理系统也不是那种动不动就要上微服务的重型项目而是一个恰好能覆盖前端交互、后端接口、角色权限、业务状态机、并发控制、订单支付建模的完整全栈业务系统。尤其是拍卖业务里“同一件商品多人同时出价”这个场景往深了能聊并发往浅了也能用一条乐观锁SQL糊弄过去非常适合作为答辩时有内容可讲的毕设项目。这个系统能做什么简单说卖家发布拍卖品并设置起拍价、加价幅度、保证金和拍卖时长买家浏览正在竞拍的商品在倒计时结束前不断出价系统自动判断出价是否有效、何时成交拍卖结束后生成订单并进入支付流程管理员在后台完成商品审核、用户管理、订单状态监控和基础数据统计。适合谁计算机专业本科毕业生特别是Java技术栈、对Spring Boot有一点基础但还没独立做过完整项目的同学。如果你看完这篇文能少走一半弯路。1. 项目概述与核心需求拆解1.1 这个题目为什么适合做毕业设计先说个很多同学都没意识到的点选毕设题目不是选一个“看起来高级”的而是要选一个“开发量适中、技术点能讲清楚、你三个月内能写完”的。拍卖管理系统恰好满足这三条。从功能维度看它包含了用户系统注册登录、个人信息、密码修改、商品系统发布、审核、上下架、分类检索、交易系统竞拍出价、保证金、订单支付、发货收货、运营系统后台统计、报表图表。这不是一个单纯增删改查的项目——对比常见的“学生管理系统”“图书管理系统”拍卖系统多了一个“竞拍时间窗”的核心业务逻辑这让它有了天然的讨论深度。从技术维度看Spring Boot MySQL MyBatis-Plus 是当前毕设最常见的技术组合能帮你把后端框架、ORM、前端页面串起来。再加点JWT做前后端分离的登录认证用WebSocket做拍卖页面的实时出价推送用定时任务做拍卖自动结束用Redis或乐观锁处理并发出价这套组合下来论文里的“关键技术介绍”就能写得很充实。还有一个隐藏好处这个题目的变体非常多。你可以把它改成二手闲置拍卖、校园二手拍卖、农产品竞拍、艺术品拍卖、纪念币拍卖……核心代码几乎不用动只换业务文案和部分字段就能变成一篇“有自己特色”的题目。同类的办公用品管理系统、企业资产管理系统也是同一个套路万变不离其宗。1.2 拍卖系统的核心角色与功能清单做系统设计的第一步不是写代码而是先画清楚“谁在用这个系统”。拍卖管理系统里客户端的角色可以分成三类服务端的管理角色尽可能打成一套后台。买家侧功能注册登录登录后能修改个人信息和密码浏览拍卖大厅按分类、起拍价、结束时间倒序/升序筛选商品对“进行中”的拍卖商品出价出价金额必须大于当前价且满足加价幅度竞拍成功后在个人中心看到待支付订单去支付或放弃根据规则决定是否扣保证金查看个人出价记录、支付记录、收货记录卖家侧功能注册后申请成为卖家管理员审核开通资格发布拍卖品填写名称、详情、图片、起拍价、加价幅度、保证金、拍卖时长查看自己发布的商品列表、当前竞价情况、拍卖结果拍卖成交后处理订单发货、查看买家确认收货情况管理员侧功能卖家资质审核、商品审核违规商品下架用户禁用/启用、拍卖订单的异常处理比如买家弃标基础数据管理公告发布、拍卖分类维护数据看板今日成交额、参与人数、出价次数、成交率等简单统计说到这里我要强调一点很多同学的毕设功能清单会写得特别多什么“推荐算法”“消息推送”“多人连麦”都往上堆。别这样。答辩时评委不会因为你功能多给你加分但会因为你功能做不完、代码里全是空方法而扣分。先把这个功能清单跑通再去想炫技。1.3 功能权限矩阵角色和功能可以整理成下面这张表画ER图和数据权限时直接对照着用功能模块买家卖家管理员注册/登录有有后台专用账号商品浏览/搜索有有有发布拍卖品无有无商品审核无无有参与竞拍出价有无无查看出价记录有自己的有自己商品下的有全部订单支付/确认收货有有发货有异常处理用户管理无无有数据统计无部分有有同学可能会问为什么卖家不能拍卖自己的商品从业务规则上讲如果允许卖家出价会出现“自己抬价”的情况所以系统设计上要做限制——出价接口校验当前用户不能是拍卖品的发布者。这个很小的细节评委问起来你如果能答上是很加分的。2. 技术选型与架构设计2.1 Spring Boot版本选择不要一上来就选3.x这是第一个大坑。很多同学一打开官网看到Spring Boot最新版本是3.x就直接用最新版做项目结果JDK不兼容、依赖报错环境就搞了半个月。这里我的建议非常直接毕设系统优先选Spring Boot 2.7.x JDK 8的组合。为什么Spring Boot 3.x基于Spring Framework 6强制要求JDK 17及以上很多第三方组件比如旧的MyBatis晚了一步、某些分页插件早期版本、某些OSS SDK和网上搜的教程都是针对2.x写的你照抄代码报错后查资料会非常痛苦。而Spring Boot 2.7.x处于2.x的末期版本功能稳定网上资料最多腾讯云/阿里云的学生机部署教程绝大多数也是基于2.x的。顺带回答一个常见困惑Spring Boot的2.3.x、2.4.x、2.6.x到底有什么区别如果你去看官方Release Notes主要变化集中在配置项调整和内部机制上2.3之后spring.factories逐步被AutoConfiguration.imports替代影响不大2.4之后配置文件通过spring.config.import的方式支持多配置文件导入这个改动影响的是“如何读取多个yml”2.6之后循环依赖默认不允许了以前两个Service互相Autowired还能跑在2.6及以上版本启动直接报错2.7之后一些过时的配置删除Spring Security 5.7开始支持自定义Filter那一套新写法所以如果你参考的代码是2.3/2.4的想升级到2.6/2.7大概率要调整的是application.yml里的配置路径和循环依赖问题解决方案下面会讲。还有一个同学在评论区问过Spring Boot 3和Python的FastAPI哪个更适合做毕设后端我的看法是你要是Python体系FastAPI完全OK写起来还快但如果你已经选了“基于Spring Boot”这个题目就老老实实用Java。答辩时评委看到你的题目、论文、代码是自洽的比什么都强。2.2 IDEA社区版搭建Spring Boot项目的两种方式另一个高频问题就是intellij idea社区版怎么用spring boot社区版是免费的但不带Spring Initializr新建项目时发现没有Spring Boot选项直接把很多人卡住了。两个解决办法方式一官方网站生成项目包打开Spring Initializr官网start.spring.io填好Group比如com.example、Artifact比如auction、选择Java版本和Spring Boot版本选2.7.x右侧勾选依赖Spring Web、Spring Data JPA、MySQL Driver、Lombok如果是前后端分离项目暂时不用勾Thymeleaf。点击Generate后会下载一个ZIP压缩包解压后用IDEA打开即可。方式二Maven工程自己写pom.xml如果你已经有一个空Maven工程也可以直接在pom.xml里复制以下核心依赖保存后等待Maven下载依赖即可parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent properties java.version1.8/java.version /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-jpa/artifactId /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies注意spring-boot-starter-parent里的版本号一改下面所有依赖版本都会被统一管理省去自己配版本号的麻烦。IDEA社区版对Maven项目是完整支持的后续开发体验和旗舰版差异不大。如果你的同学有旗舰版直接让他在New Project里选Spring Initializr生成后再发给你打开也行这个不涉及版权问题只是生成方式不同代码是完全一样的。2.3 项目分层与第三方接口放哪Spring Boot项目最常用的分层方式是controller → service → mapper(dao) → entity控制层只做参数接收和响应封装业务逻辑放在service层数据库操作由mapper完成。实体类entity对应数据库表DTO/VO对应接口入参和出参。分层的核心目的是“可替换性”。举个例子你的出价逻辑里要校验当前价这个校验可能涉及Redis缓存、数据库查询、锁操作。如果全写在Controller里接口一多就变成一团浆糊写在Service里Controller永远保持瘦身测试也好写。还有一个比较高级的问题Spring Boot对外提供的接口给第三方调用应该放在哪里是单独服务还是放在对应的服务里拍卖系统里最典型的第三方接口是支付回调。比如支付宝异步通知会主动调你的接口告诉你“这笔订单支付成功了”。这种接口的特点是不能登录校验因为第三方没有你的JWT Token所以你在设计时要区分给前端用的接口/api/auction/list、/api/order/pay走登录鉴权给第三方用的回调接口/open/callback/alipay、/open/callback/wechat走签名验签不走登录拦截在我的项目里我是为回调接口单独建了一个open包类名用OpenPayController然后在Spring Security的配置里放行/open/**路径。如果业务复杂度不高不建议毕设阶段单拆一个微服务你可以在Controller层用包名区分然后在配置类里做不同路径的过滤规则这个做法比拆服务更符合“设计合理但不过度设计”的原则。2.4 数据库设计要点数据库表是整个系统的地基。常见的表设计如下表名核心字段说明userid, username, password, role, balance, status用户表role区分买家/卖家/管理员auction_itemid, seller_id, title, description, cover_image, category_id, start_price, bid_increment, deposit, start_time, end_time, current_price, status, version拍卖品表status枚举待审核/审核通过/竞拍中/已成交/已流拍bid_recordid, auction_id, user_id, bid_price, create_time出价记录表每次出价都插入一条ordersid, order_no, auction_id, buyer_id, seller_id, amount, status, create_time订单表拍卖结束后生成payment_recordid, order_id, pay_no, amount, channel, status, callback_time支付记录表categoryid, name, sort拍卖品分类表这里有两个设计上的关键点。第一出价记录表为什么要保留每次出价而不是只存拍卖品当前价因为如果只存当前价一旦出现纠纷比如用户说“我明明出了1000怎么成交价显示800”你就没有任何审计数据可查。保留每次出价还能用来做用户行为分析、防止恶意抬价识别这是“审计”思维这也是答辩时能聊的亮点。第二数据库金额字段用DECIMAL(10,2)Java实体用BigDecimal千万不要用double或float。浮点数在二进制中无法精确表示比如0.10.2在Java里等于0.30000000000000004而拍卖出价、保证金、支付金额都涉及真金白银的精度用错类型就等着被评委纠正吧。时间字段建议用LocalDateTime避免使用java.util.Date前者有更清晰的API并且能自动处理时区问题。数据库连接串建议加上serverTimezoneAsia/Shanghai否则MySQL 8默认时区和本地不一致时你存进去的时间和查出来的时间可能差8小时。3. 核心功能实现与实操3.1 卖家发布拍卖品的参数校验逻辑商品发布是卖家端的第一步操作。前端表单提交标题、描述、图片、分类、起拍价、加价幅度、保证金、拍卖时长。后端要做的不只是“存进去”而是把业务规则提前拦截在校验阶段。核心校验点起拍价必须大于0加价幅度必须大于0保证金建议在起拍价的5%-20%之间拍卖时长至少要30分钟防止时差导致来不及出价最长不限标题长度图片大小限制文件上传时控制单张2MB以内同一卖家同一时间不能发布“标题完全相同”的拍卖品防止恶意重复铺货我遇到过一个问题前端提交的“开始时间”和“结束时间”直接用HTML的datetime-local控件但传到后端变成ISO格式字符串。后来统一约定前端传时间戳毫秒后端用LocalDateTime接收这样避免了不同浏览器日期格式差异导致的解析异常。如果你不想用时间戳后端可以使用DateTimeFormat(pattern yyyy-MM-dd HH:mm:ss)来指定格式但要注意前后端格式必须完全一致。给一段Service层的发布逻辑伪代码Transactional(rollbackFor Exception.class) public Long publish(AuctionItemCreateDTO dto) { // 基础校验 if (dto.getStartPrice().compareTo(BigDecimal.ZERO) 0) { throw new BizException(起拍价必须大于0); } if (dto.getBidIncrement().compareTo(BigDecimal.ZERO) 0) { throw new BizException(加价幅度必须大于0); } // 时间校验结束时间必须晚于开始时间且至少间隔30分钟 if (dto.getStartTime().isBefore(LocalDateTime.now().plusMinutes(5))) { throw new BizException(开始时间必须晚于当前时间5分钟后); } if (dto.getEndTime().isBefore(dto.getStartTime().plusMinutes(30))) { throw new BizException(拍卖时长不能少于30分钟); } // 保存拍卖品 AuctionItem item new AuctionItem(); BeanUtils.copyProperties(dto, item); item.setCurrentPrice(dto.getStartPrice()); item.setStatus(AuctionStatus.PENDING_REVIEW); item.setVersion(0); auctionItemMapper.insert(item); return item.getId(); }注意Transactional一定要指定rollbackFor Exception.class不然运行时异常虽然能回滚但某些特定异常如受检异常不会触发事务。这是很多同学没注意到的细节。3.2 出价并发控制别让你的起拍价被秒两次出价是拍卖系统的灵魂也是最容易出Bug的地方。先说问题场景拍卖品当前价1000元加价幅度100元两个买家同时点了出价1100元后端两个线程同时读到当前价1000都判断“1100 1000 100”成立两个人都写入成功。数据库里的当前价变了两次但记录里只有一条是最终有效的——这就是经典的并发超卖。解决思路有三种我按毕设适用的优先级排列。方案一数据库乐观锁最推荐在auction_item表加一个version字段更新当前价时带条件更新UPDATE auction_item SET current_price #{newPrice}, version version 1 WHERE id #{auctionId} AND version #{oldVersion}如果返回的影响行数为0说明在本次出价过程中商品已经被其他人先出价了此时购买当前价已变化需要提示用户重新出价。这个方案不需要引入额外中间件光是MySQL就能解决是性价比最高的选择。方案二Redis分布式锁如果系统部署了Redis可以给每个拍卖品设置一个锁keylock:auction:商品ID。用户出价前先尝试加锁Boolean locked stringRedisTemplate.opsForValue() .setIfAbsent(auction:lock: auctionId, userId.toString(), Duration.ofSeconds(5)); if (!Boolean.TRUE.equals(locked)) { throw new BizException(系统繁忙请稍后重试); } try { // 执行价格校验和更新 } finally { stringRedisTemplate.delete(auction:lock: auctionId); }这个方案要注意锁的过期时间设置。如果过期时间太短出价逻辑还没执行完锁就自动释放了太长又会造成锁无法在异常情况下快速恢复。一般设置3-5秒就够。方案三同步锁单机部署时也可以用本地锁比如用ConcurrentHashMap维护每个商品ID对应的锁对象出价时synchronized给对应商品加锁。但这只适合单实例如果项目部署成多个副本这个锁就失效了。所以毕设里要么用乐观锁要么用Redis分布式锁第三方案了解即可。我实际开发时用的是“异常捕获重试”的体验优化当乐观锁更新失败时不直接报“操作失败”而是重新查询最新价格和版本号提示用户“最新价已更新为1200元请确认是否继续出价”这个重试按钮会让用户觉得系统很智能也答辩时展示的功能完整度更高。3.3 倒计时、实时推送与WebSocket回调配置拍卖系统没有倒计时就没有紧张感。倒计时有两种实现方式一种是前端根据结束时间自己减另一种是后端在拍卖结束时主动推送给前端。大多数情况下前端用JS定时器显示剩余时间即可但一旦拍卖“结束”需要后端来最终裁决并更新状态。后端结束拍卖最简单的方式是使用Spring自带的定时任务。在启动类上加EnableScheduling然后写一个定时扫描方法Component public class AuctionCloseJob { Scheduled(fixedDelay 10000) // 每10秒执行一次 public void closeExpiredAuctions() { ListAuctionItem expiredItems auctionItemMapper .selectExpiredAndRunning(LocalDateTime.now()); for (AuctionItem item : expiredItems) { closeAuction(item); } } }如果你的项目用了Redis的过期事件还有一种方案是给每个拍卖品设置一个过期key过期后触发回调。但这个方案有延迟且不保证100%触发不比定时扫描强。定时扫描10秒一次对拍卖场景来说足够了而且实现简单。关于实时的出价推送Spring Boot集成WebSocket的yml配置经常让新手困惑。实际上Spring Boot 2.7的WebSocket配置不靠yml就能跑通主要是三步引入依赖spring-boot-starter-websocket写一个WebSocketConfig配置类注册WebSocketHandler实现TextWebSocketHandler的handleTextMessage方法处理前端传来的订阅消息给一个最简单的yaml配置片段其实大部分WebSocket配置需要是Java Configyml里主要配置服务端口server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/auction?serverTimezoneAsia/ShanghaiuseUnicodetruecharacterEncodingutf8 username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver注意driver-class-name在MySQL 8之后要写成com.mysql.cj.jdbc.Driver之前老教程里写的com.mysql.jdbc.Driver在新版本驱动下已经过时了。这也是spring boot集成WebSocket yml配置里最常见的一个环境问题。如果你想做得更“高级”一点可以在WebSocket连接建立后把当前拍卖品的出价信息群发给订阅该商品的所有客户端。实现上就是维护一个ConcurrentHashMapString, WebSocketSessionkey是商品IDvalue是会话集合出价成功后遍历并推送最新价格。这个做法比使用STOMP协议更容易理解代码量少效果也直观。3.4 订单生成与支付状态机拍卖结束后系统要自动生成订单。这里有一个状态机的设计问题订单的状态流转不能乱跳。我的状态机定义是待支付 - 已支付 - 已发货 - 已完成 | | | - 已关闭超时未支付 - 已关闭买家明确放弃拍卖成交后生成订单状态为“待支付”同时给买家一个支付倒计时通常24小时内支付。超时未支付则订单关闭拍卖品状态回滚为“已流拍”或“重新上架”并按规则扣罚保证金。这里要注意订单要有幂等性。支付回调可能发送多次如果你的代码不去重同一个订单被重复处理两次就会出现重复发货、重复入账等问题。解决方式是给订单表加一个pay_status字段处理回调时先用UPDATE orders SET pay_status PAID WHERE id ? AND pay_status PAID_PENDING影响行数为0说明已经处理过了直接跳过。支付对接建议用支付宝沙箱或者微信支付沙箱。如果你不想折腾复杂的商户配置可以用“模拟支付”按钮在前端点支付时跳转到支付确认页点“确认支付”后直接调用后端接口把订单设置为已支付并记录一条payment_record。这样做在演示时最不会出岔子而且论文里可以说“本系统预留了支付宝/微信支付接口为安全起见在演示环境使用模拟支付”。如果评委追问你能说出支付宝异步通知验签的流程这个回答就圆过去了。3.5 登录认证与Bean注入控制传统的Session会话在前后端分离项目中体验不佳所以建议使用JWT。流程是登录成功后生成Token返回给前端前端每次请求放在Authorization头里后端用拦截器解析Token并获取当前用户。Spring Security的配置经常让人一头雾水最简单的做法是自定义一个JwtAuthenticationFilter继承OncePerRequestFilter从请求头解析Token在SecurityConfig里放行登录接口、注册接口、/open/**回调接口其余接口全部走鉴权很多同学会在配置Spring Security时发现前端请求一直403或401。多半是没配置cors()和csrf().disable()。前后端分离项目用Token认证后CSRF本身存在的意义就下降了直接关闭即可。再提一个热搜词“spring boot bean注入控制”。很多同学刚接触Spring时喜欢在类属性上用Autowired直接注入比如Autowired private UserService userService;这种方式虽然能跑但有个问题字段被final修饰不了测试时不方便注入Mock对象还容易在多个类之间形成循环依赖。Spring Boot 2.6以后默认禁用了循环依赖如果你的A Service注入了B ServiceB Service又注入了A Service启动时会直接抛出BeanCurrentlyInCreationException。我习惯的做法是构造器注入Service public class AuctionServiceImpl implements AuctionService { private final AuctionItemMapper auctionItemMapper; private final BidRecordMapper bidRecordMapper; // 无需再加AutowiredSpring会自动按构造函数参数注入 public AuctionServiceImpl(AuctionItemMapper auctionItemMapper, BidRecordMapper bidRecordMapper) { this.auctionItemMapper auctionItemMapper; this.bidRecordMapper bidRecordMapper; } }这样写的好处是依赖关系清晰、final字段不可变、单元测试时可以直接new一个实例并传Mock对象。如果你在工程里看到“字段注入”导致项目启动报错优先检查是不是循环依赖。4. 常见问题与排查技巧实录4.1 从“第一个Spring Boot程序”开始的环境问题很多同学在正式开发前连“第一个Spring Boot程序”都没跑通过。要我说做拍卖系统之前先花半小时跑通一个最简单的Spring Boot项目确认环境和IDEA配置没问题再叠业务代码。跑第一个Spring Boot程序的流程创建Spring Boot项目只勾选Spring Web在主类main方法同包下新建一个HelloController启动运行类浏览器访问localhost:8080/hello看到“Hello Auction”就算成功如果你在这个阶段就报错最常见的问题有三个。第一端口被占用日志里出现Port 8080 was already in use解决方式是在application.yml里改成server.port8081或者用命令行netstat -ano | findstr 8080找进程杀掉。第二Maven依赖下载慢Maven默认源在国外改成阿里云镜像能快十倍。第三没有写RestController注解返回字符串变成了404这种低级错误排查时反而最花时间。有一个实用的小技巧启动类上面的SpringBootApplication自带包扫描功能它只扫描“启动类所在包及其子包”。如果你把控制器放在启动类同一个包外面Spring根本找不到你的类启动不报错但访问接口404。这是新手最难以排查的问题之一。4.2 启动失败和依赖冲突毕设项目做到中后期最容易遇到的是依赖冲突问题。比如你为了生成验证码引入了hutool工具包里面包含了一个bcprov版本而Spring Security也依赖另一个版本的bcprov两个版本冲突后启动就会报NoSuchMethodError或ClassNotFoundException。通用排查思路是看报错的第一行异常栈是哪个类在IDEA右侧Maven面板执行mvn dependency:tree查看依赖树用exclusion标签把多余的传递依赖排除掉dependency groupIdcn.hutool/groupId artifactIdhutool-all/artifactId version5.8.22/version exclusions exclusion groupIdorg.bouncycastle/groupId artifactIdbcprov-jdk15on/artifactId /exclusion /exclusions /dependency另一个常见问题是Lombok不生效。表现为代码里用了Data但是getter/setter方法在编译时找不到。原因一般是IDEA社区版没有安装Lombok插件或者没有开启Annotation Processing。解决方式是在IDEA设置里搜索“Annotation Processors”勾选“Enable annotation processing”然后重启项目。如果你用的是社区版插件在市场里也是能搜到并安装的。4.3 接口调用的“灵异”问题开发过程中你会遇到很多看似无法解释的Bug我列几个高发场景。第一个前端请求接口返回404后端控制台没有任何日志。这个90%是请求路径对不上——检查Controller上的RequestMapping和前端axios里的URL是否大小写、斜杠完全一致。第二个前端请求接口能返回数据但浏览器里看到Response Body是空的。这个大概率是跨域问题。前后端分离开发时前端跑在5500端口后端跑在8080端口需要配置跨域。最简单的方式是写一个CorsConfigConfiguration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }如果你用了Spring Security还要在Security的配置里也放行OPTIONS请求否则浏览器预检请求被拦截真实请求根本发不出去。第三个保存到数据库的时间跟页面显示的时间差了8小时。原因就是MySQL连接串没有加serverTimezone参数或者你的操作系统时区不是东八区。在jdbc:mysql://localhost:3306/auction?serverTimezoneAsia/Shanghai加上后问题一般就解决了。第四个金额明明是0.1数据库存了0.10Java读出来是0.1但和另一个0.2比较时发现不相等。这就是浮点数精度问题。你要做的不是“比较时四舍五入”而是把所有金额字段从double改成BigDecimal数据库从double改成decimal(10,2)。改完以后所有判断都用compareTo而不是equals。4.4 Spring Boot Admin监控能帮你在答辩前发现什么有人问spring boot admin到底是个什么组件值不值得在毕设里加。它是一个轻量的应用监控面板能显示HTTP接口调用量、JVM内存、线程数、日志输出等指标。如果毕设想展示“运维思维”加入Spring Boot Admin是个不错的差异化亮点。使用方式很简单新建一个admin-server独立小项目服务端开在9001端口主项目作为admin-client在yml里配置spring: boot: admin: client: url: http://localhost:9001 management: endpoints: web: exposure: include: *然后启动两个项目访问localhost:9001就能看到监控面板。虽然它对你的业务代码没有直接影响但在答辩时你可以说“系统接入Spring Boot Admin监控能够实时查看接口调用量和JVM内存占用为后续的服务运维和容量规划提供了参考。”这一句话就能把“部署和运维”这块论文内容从无写有。不过要提醒一点监控面板里的指标很多答辩时选一两个最直观的介绍即可别背一大堆术语让自己陷入被动。比如点开“HTTP接口调用量”展示出价接口在拍卖高峰期有明显波动延伸说明系统能在高并发场景下被观测这是加分项。5. 答辩经验与后续扩展方向5.1 评委爱问的几个问题答辩不是代码review评委更看重“你能不能讲清楚”。我总结了几道高频问题建议你提前准备。为什么选Spring Boot而不是SSHStruts2SpringHibernate答Spring Boot简化了Spring的配置过程内置Tomcat起步依赖让开发者专注于业务代码对比SSHSpring Boot更适合快速构建独立的微服务应用。而且生态完善社区资料多遇到问题能快速找到解决方案。你是怎么处理并发出价的答采用乐观锁方案在拍卖品表增加version字段更新时校验版本号影响行数为0则提示用户重新出价。之后可以说“如果要支持更高并发可以引入Redis分布式锁”把扩展思路也讲出来。为什么用JWT不用Session答Session需要服务端存储状态当项目需要横向扩展时多实例之间需要共享SessionJWT是无状态的Token由服务端签名生成客户端保存服务端只验签不存储能更好地支撑前后端分离和分布式部署。金额字段为什么用BigDecimal答double和float在二进制中无法精确表达可能出现0.10.2不等于0.3的问题BigDecimal使用十进制运算能精确表示金额配合数据库DECIMAL类型可以保证支付数据的准确性。5.2 这个系统后续能怎么演进毕设写完不等于结束答辩完提交完这套代码本身还可以继续演进。如果你想在简历上把它写成项目经历可以考虑下面几个扩展方向。第一个方向是引入消息队列。当一个拍卖品结束时需要同时做“生成订单”“扣减保证金状态”“推送结果通知”多个操作可以用RocketMQ或RabbitMQ把AuctionCloseEvent发出去由不同消费者分别处理。这就能在简历上写“使用消息队列实现事件的异步解耦”。第二个方向是增加定时任务的可视化配置。现在的定时任务是每10秒全表扫描如果拍卖品数量大这种扫描方式效率不高。可以把扫描改成“按结束时间建立索引分批查询”或者把即将结束的拍卖品ID缓存到Redis的ZSet中用Redis的过期机制触发结束操作。第三个方向是接入AI能力。现在的系统里商品描述和图片都是用户手动上传的未来可以接入大模型能力自动提取商品标题关键词、生成描述文案、检测违规内容。这也是当前很多管理系统都在做的“AI集成”方向说出去也不落伍。最后再分享一个我自己的习惯每做完一个模块先把该模块的“业务流程表设计关键代码”整理成文档最后论文直接有素材可用不需要在最后一周通宵补论文。写拍卖系统这种全栈项目时最难的不是某个技术点而是把业务逻辑和代码实现结合起来讲清楚。你只要把“谁在什么时间对什么商品做了什么操作”这条主线贯穿始终整个项目就立住了。
返回列表