
1. 后疫情时代的农产品供应链到底在解决什么业务问题先说个真实的场景。2022年某地临时管控期间我帮家里老人抢菜社区团购群里每天早上六点接龙菜价翻了快三倍还不一定抢得到。另一边郊区有个做蔬菜合作社的朋友跟我吐槽地里几万斤生菜卖不出去因为采购商进不来信息根本传不到有需求的人手里。生产和需求两头都急中间隔着好几道信息断层——这种情况在疫情反复的几年里反复上演不是个例而是农产品流通体系长期存在的结构性问题被极端环境放大后的结果。这个项目的出发点就是把“后疫情时代”这四个字从新闻标题变成系统的需求文档。常规的农产品电商系统核心逻辑是“把农产品挂到网上卖”跟卖衣服卖数码产品没有本质区别。但供应链系统不一样它要回答的问题不是“怎么把商品陈列得好看”而是“在需求剧烈波动、运力时断时续、产地信息不透明的情况下怎么让农产品以合理的成本、在可接受的时间内从田间地头到达消费者手里”。所以这个基于Spring Boot的农产品供应链系统设计目标非常明确围绕“产、供、运、销”四条主线把原来散落在Excel表格、微信群里、电话沟通中的订单、库存、批次、流向、车辆、价格信息统一收口到一个平台上。系统服务三类角色产地端的农户和合作社、中间环节的仓储物流管理人员、终端的分销商和社区团购团长。每一类角色看到的数据和能执行的操作都不一样但所有数据最终都汇聚到同一套逻辑里互相咬合形成一个闭环。顺带说一句这类选题在毕业设计里非常讨巧因为它的业务复杂度足够撑起一个有分量的系统但又不要求你懂高深的算法。真正的难点在于业务逻辑的梳理——需求分析做得越细后面的编码就越顺。很多同学毕设做到一半推翻重来八成是前期没把“这个系统到底干什么事”想清楚一上来就建表写代码越写越乱。这篇博文就是把我做这个系统的完整思路、技术选型、核心实现和踩坑记录摊开来讲希望能帮到正在为毕设头秃的各位。2. 需求分析比写代码更重要从主链路推导出来的功能清单2.1 先理清主链路再谈功能模块我不建议一上来就照着网上流传的“XX管理系统”模板抄模块什么用户管理、角色管理、菜单管理、日志管理——那些是系统骨架不是业务本身。骨架当然要但先想清楚业务主链路再回头补骨架顺序才对。农产品的供应链主链路是这样的产地农户/合作社→ 加工/集配中心仓储→ 干线运输 → 城市分拨 → 终端销售商超/社区团购/线上商城。每个环节都有信息产生农户要上报什么菜熟了、有多少量、什么价格集配中心要知道货什么时候到、质检结果如何、库存剩多少运输环节要记录车辆在哪、温度是否达标终端要下单、要查询到货时间、要处理损耗和退换。把这些环节逐一拆开就能推导出系统的核心模块而不是拍脑袋凑功能。我最终确定的模块边界是这样基础信息管理供应商农户/合作社档案、商品目录、仓库信息、车辆信息、客户分销商/团长档案。批次与库存管理每一批农产品从产地入库开始就有独立的批次号记录产地、采收日期、质检结果、当前所在仓库、库存余量、保质状态。这是供应链系统和普通电商系统最本质的区别——电商管SKU供应链管批次。订单管理下游客户下单系统自动匹配可用的库存批次生成出库单和配送单。订单状态跟踪到签收。仓储物流管理入库、出库、盘点、调拨以及运输任务的指派、在途状态更新、签收确认。质量溯源通过批次号反查某批货的产地信息、检测报告、运输路径形成一个可追溯的链条。后面我会详细讲实现思路。供需预警与数据看板根据历史销量、当前库存、在途订单预测未来一段时间的需求缺货或者积压时自动预警。这是整个系统里最有技术含量、也最适合在答辩时展开讲的部分。2.2 双轨订单设计为什么不能只做一套下单流程常规电商系统的订单流程只有一条线用户下单→付款→发货→收货。但农产品供应链系统在后疫情时代必须支持两种完全不同的订单模式这就是我反复强调的“双轨设计”。第一条轨是常规预售订单。分销商或者消费者在平台上浏览商品看到的是“未来某个时间段可发货”的期货逻辑。比如社区团购团长今天下单100斤西红柿系统反馈的是后天上午送达价格基于当前产地报价。因为生鲜产品不能像工业品那样有大量现货库存多数订单是“以销定采”先收集订单再向产地要货。第二条轨是应急保供订单。这是后疫情时代的特殊产物。当局部地区出现突发情况运力受限、线下渠道关闭时系统要能快速切换为保供模式由政府或社区组织发起定向采购系统自动锁定指定产地或仓库的储备库存优先匹配运力全程跟踪配送进度甚至要支持“按户分装”这种颗粒度更细的订单拆分。这两条轨的数据模型必须从一开始就设计成一张订单主表加多个扩展字段而不是建两张表。比如订单类型字段区分“常规”和“保供”保供订单额外记录发起方、关联社区、配送时限要求常规订单则走标准的价格计算和支付流程。后面我会把表结构详细展开这里先记住一个原则业务模式的分叉不要用表结构的分叉来解决用字段和状态机来解决否则后续统计和扩展会非常痛苦。2.3 从功能列表反推角色权限模块清单确定之后再回过来设计角色和权限就顺理成章了。系统里我划分了四个角色角色核心操作关心的数据产地端农户/合作社维护商品信息、上报产量、查看采购订单、确认供货我的货卖了多少、还剩多少、钱什么时候结仓储物流管理员入/出库操作、批次管理、质检结果录入、车辆调度库存总量、库存分布、运力是否够用分销商/团长下单、查看订单状态、签收、提交售后货到哪了、到货质量、下次什么时候能补货平台管理员系统配置、供需预警查看、全局数据看板、价格审核全局供需平衡、异常预警、各环节效率权限设计上我没有用过于复杂的RBAC基于角色的访问控制模型。Spring Security是必须上的但权限粒度到角色级别就够用了不需要细到按钮级别。四个角色对应四套菜单外加一个管理员拥有全部权限做毕设的体量完全足够。非要把权限粒度搞得比公司OA还细反而给自己挖坑。3. 核心数据模型设计批次、双轨订单和预警计算3.1 商品批次表的设计思路这一步是整个项目的地基我当初在这上面花的思考时间最多。农产品供应链的库存管理核心实体不是“商品”而是“批次”。同一款西红柿今天从A合作社收的和三天后从B合作社收的虽然商品ID相同但产地不同、采收时间不同、质检结果不同、存放库位不同必须当作两个独立记录来管理。批次表的核心字段我设计成这些CREATE TABLE product_batch ( id BIGINT PRIMARY KEY AUTO_INCREMENT, batch_code VARCHAR(32) NOT NULL COMMENT 批次编号规则产地编码日期流水, product_id BIGINT NOT NULL COMMENT 商品ID, supplier_id BIGINT NOT NULL COMMENT 供应商农户/合作社ID, origin_place VARCHAR(100) COMMENT 产地, harvest_date DATE COMMENT 采收日期, quantity DECIMAL(10,2) NOT NULL COMMENT 初始数量公斤/件, remain_quantity DECIMAL(10,2) NOT NULL COMMENT 剩余数量, unit_price DECIMAL(10,2) COMMENT 批次采购单价, quality_status INT DEFAULT 0 COMMENT 质检状态0待检 1合格 2不合格, quality_report_url VARCHAR(255) COMMENT 质检报告附件地址, warehouse_id BIGINT COMMENT 当前所在仓库, storage_location VARCHAR(50) COMMENT 库位编码, status INT DEFAULT 0 COMMENT 批次状态0在库 1锁定 2出库完 3报损, expire_date DATE COMMENT 建议保质期/最佳销售日期, created_time DATETIME, updated_time DATETIME ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品批次表;这个表有个很容易被忽略的设计点remain_quantity是加减操作的核心字段每次出库、报损、退货都必须在这个字段上做扣减同时通过一张流水表记录每一次变动。扣减操作一定要放在事务里配合行级锁否则多订单并发出库时会超卖。我开发时先用了简单的“查询剩余数→判断够不够→更新剩余数”三段式压测时发现高并发下会出现同一批次被两个订单同时扣减的情况后来改成在更新语句里直接做条件扣减UPDATE product_batch SET remain_quantity remain_quantity - #{quantity} WHERE id #{batchId} AND remain_quantity #{quantity}一行SQL搞定原子操作受影响行数为0说明库存不足。这个写法在生鲜分秒必争的场景下很实用建议记下来。3.2 双轨订单的数据建模与状态机订单主表我设计成可同时容纳常规订单和保供订单的“大一统”结构CREATE TABLE supply_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL, order_type INT NOT NULL COMMENT 1-常规订单 2-保供订单, customer_id BIGINT NOT NULL COMMENT 客户ID分销商或社区, customer_type INT COMMENT 客户类型1-分销商 2-社区团长, total_amount DECIMAL(10,2) COMMENT 订单总金额, order_status INT NOT NULL COMMENT 状态机0待支付 1已支付/待确认 2已确认 3配货中 4运输中 5已签收 6已取消 7售后处理中, source_order_id BIGINT COMMENT 保供订单可关联上游指令单, community_id BIGINT COMMENT 保供模式关联社区ID, delivery_deadline DATETIME COMMENT 保供模式要求送达时间, delivery_address VARCHAR(255), contact_phone VARCHAR(20), create_time DATETIME, update_time DATETIME, create_by BIGINT, INDEX idx_customer_time(customer_id, create_time), INDEX idx_status_priority(order_status, delivery_deadline) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单主表;订单状态机是整个系统的业务中枢。我用了整数状态而非字符串状态一是省存储二是排序和判断都方便。状态流转规则集中在Service层校验——比如只有“已确认”状态才能生成出库单“保供订单”可以跨过“待支付”直接进入“已确认”状态跳转规则写在枚举里统一管理避免各层代码到处写魔法数判断。订单表和批次表之间的关联通过订单明细表完成这样一份订单可以同时匹配多个批次的货源——应急保供时从三个不同仓库调货凑齐一单是很常见的事。这种“多批拼单”的灵活性恰恰是供应链系统必须支持的业务场景。3.3 供需预警的计算逻辑预警模块是答辩时最能拉开分数差距的部分也是实际项目里最有使用价值的模块。核心思路是基于时间窗口的滚动预测根据过去N天的平均日销量结合当前库存余量和在途订单量计算出“预计可供天数”再跟安全阈值对比。我用的计算模型是预计可供天数 当前库存余量 在途补货量 / 预测日销量 缺货风险阈值预计可供天数 补货前置周期 2天 积压风险阈值预计可供天数 15天 且 商品保质期较短预测日销量不用搞花哨的机器学习移动平均法加趋势修正就够了。我取过去7天的数据做加权平均越靠近当天的权重越高public BigDecimal predictDailySales(Long productId) { // 取近7天的每日销量按日期从近到远赋权 7,6,5,4,3,2,1 ListDailySalesVO list orderDetailMapper.selectDailySales(productId, 7); BigDecimal totalWeight BigDecimal.ZERO; BigDecimal totalSales BigDecimal.ZERO; for (int i 0; i list.size(); i) { BigDecimal weight BigDecimal.valueOf(7 - i); totalSales totalSales.add(list.get(i).getSales().multiply(weight)); totalWeight totalWeight.add(weight); } return totalSales.divide(totalWeight, 2, RoundingMode.HALF_UP); }预警任务用定时任务触发每天凌晨和中午各跑一次扫描所有活跃商品批次命中阈值就生成预警记录同时消息推送给平台管理员和相关的仓储人员。底层用Spring Boot自带的Scheduled单机跑就够没必要上分布式任务调度平台。这个模块的价值在于它把“供应链管理水平”从一个抽象概念变成了可量化的指标。答辩时你可以用一组真实数据演示某商品库存还能撑3天而采购前置周期是5天系统自动预警管理员一键把预警转成采购单——这种业务闭环的演示效果比单纯展示CRUD强太多了。4. Spring Boot技术选型的取舍逻辑与关键配置4.1 Spring Boot在这个场景里解决了什么问题说句不算夸张的话这类管理系统选Spring Boot几乎是当前Java技术栈下的最优解。原因不是Spring Boot本身多高级而是它把过去Java Web开发里最繁琐的“配置地狱”给终结了。传统的SSHSpring Struts Hibernate或者早期的Spring MVC项目光配置文件就要写好几百行XML里密密麻麻的bean定义、数据源配置、事务代理新手光是把环境跑起来就得折腾一周。Spring Boot最核心的贡献是自动装配机制——它通过EnableAutoConfiguration注解触发spring.factories中声明的各种AutoConfiguration类根据classpath下的依赖自动创建和配置Bean。什么意思呢你在pom.xml里引入spring-boot-starter-data-redisSpring Boot检测到classpath里有Redis的客户端类就自动帮你创建RedisTemplate等一系列Bean你只需要在application.yml里填连接地址和密码就行。自动装配机制在毕设答辩里也是高频考点。面试官通常会追问“讲一下Spring Boot自动装配的原理。”回答的要点就三步Spring Boot启动时SpringBootApplication组合了EnableAutoConfiguration后者通过AutoConfigurationImportSelector加载META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中列出的所有自动配置类每个自动配置类上用ConditionalOnClass、ConditionalOnMissingBean等条件注解判断是否需要真的装配最后执行配置类里的Bean方法创建对象。能把这个链路讲清楚这一题就稳了大半。4.2 关键依赖选型与版本避坑我用的组合是Spring Boot 2.7.x MyBatis-Plus MySQL 8.0 Redis MinIO WebSocket Spring Security JWT。这套组合在国内Java毕设里属于主流中的主流网上资料多、遇到问题搜得到、老师也认可。版本上有一个重要建议如果你不是对Java 17有强烈需求Spring Boot用2.7.x而非3.x。Boot 3.x基于Jakarta EE规范包名从javax换成了jakarta很多老版本的依赖不兼容网上搜到的解决方案大部分还是针对2.x的。2024年做毕设时用3.0.6踩了Activemq整合的坑花了两天才解决。学术项目求稳不求新选择稳定的2.7.18就好。我在本地实际开发时用的全套版本参考组件版本说明Spring Boot2.7.18稳定社区资料丰富JDK1.82.7支持的经典版本配8G内存的电脑毫无压力MyBatis-Plus3.5.3.2单表CRUD不需要写SQL节省大量时间MySQL8.0注意连接参数nullCatalog让MyBatis-Plus正确处理多租户Redis6.x会话共享、缓存热点数据如商品列表MinIO8.4.x自建对象存储用于质检报告、商品图片Spring Security JWT配合Boot版本前后端分离认证方案一个很常见的坑是MySQL连接时区问题。在application.yml里必须加上serverTimezoneAsia/Shanghai否则数据库时间和你本地时间差八个小时排查起来让人崩溃。4.3 前后端分离和部署方案前端我用Vue 2 Element UI。这个选择有点“过时”但非常务实Vue 2的教程和组件库资料比Vue 3多一个数量级本科生做毕设完全没必要用Vue 3的组合式API死磕。前端工程跑在8080端口后端Spring Boot跑在8081通过nginx做反向代理解决跨域开发环境下用Vue CLI的proxy代理。部署时我用Docker Compose把MySQL、Redis、MinIO、后端jar包、前端Nginx五个容器编排起来一键启动。Nginx里把/api前缀的请求转发到后端服务同时托管前端静态文件。这样一个命令就能在任意Linux服务器上复现整个环境写进部署文档里也很加分。有一点必须提醒系统里的文件上传功能一定要接对象存储MinIO或OSS不要往项目本地目录写文件。因为Docker容器一旦重建本地文件就丢了而且Tomcat运行目录下的临时文件是不可控的。MinIO的接口兼容S3协议SDK封装得很好几行代码就能完成上传。我在做项目时把质检报告、商品图片、溯源凭证都交给了MinIO存储和业务分离省了很多事。5. 核心业务场景的实现思路预警、溯源、运力调度5.1 供需预警模块的完整实现链路前面讲了预警的计算模型这节把完整实现链路串一遍。预警任务由Scheduled定时触发核心流程分四步扫描商品→计算指标→判断阈值→生成预警。用Java写一个定时任务注意这里的逻辑要测试。第一步扫描所有在售商品列表分页拉取避免一次撑爆内存。第二步对每个商品调用predictDailySales方法得到预测日销量。第三步用当前库存和在途补货量计算可供天数。第四步命中阈值则插入预警表并通过WebSocket推送实时消息给前端。完整代码示例Component public class SupplyWarnTask { Autowired private ProductService productService; Autowired private WarnService warnService; Autowired private WarnWebSocket webSocket; Scheduled(cron 0 0 2 * * ?) public void checkSupplyWarning() { ListProduct products productService.listAllEffective(); for (Product product : products) { BigDecimal dailySales warnService.predictDailySales(product.getId()); if (dailySales.compareTo(BigDecimal.ZERO) 0) { continue; // 无销量数据的产品跳过 } BigDecimal stock warnService.getCurrentStock(product.getId()); BigDecimal transit warnService.getTransitStock(product.getId()); BigDecimal availableDays stock.add(transit).divide(dailySales, 2, RoundingMode.HALF_UP); WarnRule rule warnService.getRule(product.getCategoryId()); if (availableDays.compareTo(rule.getShortageThreshold()) 0) { WarnRecord warn warnService.createWarn(product.getId(), product.getName(), 缺货风险, 预计可供天数只剩 availableDays 天); webSocket.pushToAdmin(warn); } if (availableDays.compareTo(rule.getOverStockThreshold()) 0 product.getShelfLifeDays() rule.getPerishableDays()) { WarnRecord warn warnService.createWarn(product.getId(), product.getName(), 积压风险, 预计可供天数达 availableDays 天注意临期报损); webSocket.pushToAdmin(warn); } } } }给一个真实的模拟数据验证一下逻辑某超市西红柿近7天加权日均销量约200公斤当前库存400公斤在途订单120公斤则预计可供天数(400120)/2002.6天。如果补货前置周期是4天系统必然触发缺货预警。每天凌晨2点跑完任务后管理员早上打开系统就能看到预警点击预警一键生成采购申请单采购审核后定向发布给产地端。这条链路非常贴合疫情期间的实际操作——先预测缺口再定向组织货源避免盲目抢菜和产地滞销同时发生。5.2 溯源模块的低成本落地方式说到农产品溯源很多人第一反应是区块链、物联网设备、二维码标签高级但不好落地。我的做法更务实不做硬件聚焦信息链闭合。每一批农产品从产地上报开始就有了唯一批次号入库时录入质检结果出库时绑定运单运输全程由司机在APPWeb端化上签到更新位置状态消费者端用订单号反查批次信息即可看到产地、检测报告、采收日期、运输时长。实现上不复杂批次表存闭环所需的信息溯源只需要按“订单→订单明细→批次→供应商/质检报告”的路径做联表查询。用一个专门的溯源页展示每个环节配时间线样式展示效果就不错。GetMapping(/trace/{orderNo}) public ResultTraceVO trace(PathVariable String orderNo) { // 1. 查订单 SupplyOrder order orderService.getByOrderNo(orderNo); // 2. 查订单明细中的批次集合 ListLong batchIds orderItemService.getBatchIdsByOrderId(order.getId()); // 3. 按批次查产地、质检、物流节点信息 ListTraceNode nodes traceService.buildTraceChain(batchIds); return Result.success(new TraceVO(order.getOrderNo(), nodes)); }建议在溯源页加上“报告下载”功能把质检报告PDF从MinIO取出来返回给前端方便社区团购团长在群里转发增加消费者的信任感。这个功能在答辩演示时特别出彩——你点开一个订单产地信息、检测报告、物流路径一一呈现评委一眼就能看懂系统价值。5.3 运力调度的简化但可用方案完整意义上的智能调度需要路径规划算法但对毕设来说把分配逻辑做成“规则引擎”形式就足够有说服力了。车辆表维护运力档案车型、载重、当前状态、空闲时间段。生成运输任务时调用分配规则先按时间窗口过滤可用车辆再按载重量匹配订单若多笔订单路线相近可以拼车状态不满足就标记为“待人工调度”。规则写在Service层每一条用条件判断清晰列出方便答辩时逐条解释。java public Vehicle dispatchOrder(SupplyOrder order) { ListVehicle availableVehicles vehicleService.listAvailableByTime( order.getDeliveryDeadline()); // 按载重筛选 Vehicle matched availableVehicles.stream() .filter(v - v.getLoadCapacity().compareTo(order.getTotalQuantity()) 0) .min(Comparator.comparing(Vehicle::getLoadCapacity)) .orElse(null); if (matched null) { // 没有单车可承运时尝试拆分配送 return dispatchBySplit(order, availableVehicles); } vehicleService.lockVehicle(matched.getId()); return matched; }这个模块不复杂但必须有。因为“有调度功能”和“没有调度功能”在系统完整度上是质的差距哪怕只做最朴素的按载重优先匹配也比让用户手动选车要专业得多。6. 实打实的坑位记录从开发到答辩的高频问题排查6.1 第一个坑Spring Boot版本选择导致的依赖爆炸刚开始建项目时图新鲜用了Spring Boot 3.0.5 JDK 17结果引入MyBatis-Plus时发现它当时最新版对Boot 3适配还不稳定运行时一直报ClassNotFoundException: javax.persistence.*排查半天才发现是包名从javax改成jakarta导致的。因为网上大多数解决方案还是针对Boot 2.x整个过程非常消耗时间。后来老老实实换回Spring Boot 2.7.18 JDK 1.8所有依赖一次通过。经验总结就是毕设项目求稳不求新生产上还没大规模验证的新版本会让你的开发过程变成填坑循环。6.2 第二个坑HandlerInterceptor拦截器对文件上传请求失效我在做全局XSS防护时写了一个拦截器处理所有请求参数结果死活过滤不到PDF文件上传接口。查了一天日志发现文件上传的Content-Type是multipart/form-data参数不在request的parameterMap里而在输入流中。拦截器只能处理常规参数文件流必须单独处理。方案是在上传接口的Controller里手动校验文件内容。原本还研究过写一个全局过滤器把流读出来再传给拦截器链但要多写不少代码。毕设项目里最稳妥的做法是文件类型和大小在网关或Controller入口做校验就好不要试图用拦截器统一处理这是很多同学忽略的细节面试官如果真的懂行追问起来容易掉坑。6.3 第三个坑JAR包反编译学习某天我想参考其他项目的一个用Spring Boot写的功能模块找不到源码只拿到一个编译好的JAR包。当时想到将Spring Boot JAR反编译成项目来看。这个过程本身非常值得记录。先解压JAR里面的BOOT-INF/classes目录就是编译后的.class文件。反编译工具我用的是IDEA自带的反编译器直接打开class文件就能看代码也可以用命令行工具如CFR。反编译出来的代码里函数名、注释全部丢失变量名变成str1、str2但至少能看清逻辑调用关系对理解第三方项目的设计思路还是有帮助的。要注意反编译只能用于你拥有合法授权的项目毕设场景下引用别人的代码要谨慎涉嫌抄袭的代码不能直接抄进自己的论文查重系统——这是诚信红线。6.4 小知识JAR包里提取完整可重建的工程想恢复成一个相对完整的可运行工程操作也有讲究# 1. 解压JAR jar xf demo.jar # 2. 反编译核心class文件 # 用IDEA打开解压目录中的BOOT-INF/classesIDEA会自动反编译 # 3. 从BOOT-INF/lib拷出第三方依赖 # 获取pom依赖坐标用 mvn dependency:copy-dependencies 配合手动整理如果是Gradle早期项目构建的Boot项目可能在MODULE结构上略有差异但思路一样。从JAR还原不是目的目的是学习别人的分层设计。我在研究一个开源项目的代码时报错找不到原始配置最后靠反编译才知道是ConfigurationProperties的字段命名问题——这类手段在调试阶段对排查问题确实很有帮助。6.5 MyBatis-Plus的多租户字段连接问题MySQL 8.0 连接参数里不加nullCatalogMeansCurrenttrueMyBatis-Plus在3.5.x版本下执行某些与information_schema相关的逻辑会拿到错误的结果。这个坑很难查因为是底层框架内部的行为差异。加一行配置就能解决spring: datasource: url: jdbc:mysql://localhost:3306/agri_supply?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltruenullCatalogMeansCurrenttrue6.6 WebSocket鉴权这件事没有那么简单预警推送用到了WebSocket但WebSocket握手时的JWT校验是独立于HTTP请求的Session里的用户信息不会自动注入。需要在握手拦截器里手动解析tokenpublic class WebSocketAuthInterceptor implements HandshakeInterceptor { Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, MapString, Object attributes) { String token request.getURI().getQuery().split(token)[1]; if (JwtUtil.verify(token)) { attributes.put(userId, JwtUtil.getUserId(token)); return true; } return false; } }我最初没做这个校验前端拿着无效token也能连上推送通道还好在安全测试阶段发现了。毕设答辩时主动说出这个问题和解决方案会让评委觉得你的安全意识是过关的。7. 开发流程复盘如何把毕设节奏控制得从容最后复盘一下整个项目的开发节奏和心态问题。很多同学做毕设最大的问题不是技术不行而是节奏失控——前期改需求改到天荒地老后期熬夜补功能写到一半推翻重来。我的建议是控制需求范围是毕设成功的第一要素。同样的题目你可以做十个模块也可以把四个模块做深做透。评委评估的标准不是模块数量而是你有没有把一个核心业务逻辑讲明白、做扎实。所以我最终保底的核心闭环是“预测需求→生成采购→入出库管理→物流配送→最终追溯”其余会员积分、优惠券、活动促销这些锦上添花的功能一概不做做了也不会给你加多少分但出了问题会让你多熬几个通宵。进度安排上我给个参考前2周需求分析、数据库设计、接口文档定义第3~4周搭建前后端骨架跑通登录认证和主页面布局第5~7周核心模块——批次库存、订单双轨、出入库第8~9周预警模块、溯源模块、WebSocket推送第10周联调收尾补Excel导入导出、部署上线最想单独拿出来说的一点是写代码之前先把数据库表设计定稿。我亲眼见过太多同学因为表结构改来改去导致Mapper层代码推倒重写。表结构一旦定下来业务字段明确后续所有编码都是往既定管道里填内容速度快且不会迷茫。后端项目里我顺手还加了一些提升体验的小功能Excel文件导入导出导出每日订单报表给合作社对账、用Spring Task定时清理超时未支付订单、统一异常处理返回标准Result结构体、操作日志记录关键业务变更。这些功能代码量不大但让系统的完成度显得高出一个档次写进毕业论文的工作量描述里也非常好看。希望这篇复盘能让你少踩几个坑。如果你正准备做Spring Boot相关的毕设别急着敲代码先花一周时间把业务主链路画清楚把所有表结构设计成你闭着眼都能默写出来的程度后面的开发就是水到渠成的事。