ARTICLE DETAIL

资讯详情

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

Spring Boot农牧产品仓储管理系统:批次、保质期与先进先出实战

Spring Boot农牧产品仓储管理系统:批次、保质期与先进先出实战 做毕业设计选“仓储管理系统”这个方向的人很多但绝大多数作品都停留在“进销存增删改查”的层面做完自己心里都没底。如果你正在做或者准备做springboot面向农牧产品批发商的仓储管理系统那这篇内容正好对口。它不是那种泛泛的“SSM框架实现超市库存管理”的模板项目而是把农牧产品批发这个细分场景真正落到了数据库表设计、业务流程和代码实现上。我会把这个系统从需求分析、表结构设计、核心业务实现到答辩要点完整拆给你看重点讲清楚为什么农牧产品仓储不能直接套用通用WMS的设计思路。1. 核心领域拆解为什么农牧产品仓储不能照搬通用WMS很多同学一拿到“仓储管理系统”这个题目第一反应就是去抄一个商品管理加订单管理的模板然后把“商品名称”改成“农产品名称”就算完事。这么做不是不行但答辩时老师随便问一句“你的系统怎么处理临期产品”你就容易卡壳。农牧产品批发商的仓储管理和普通电商仓库有本质区别主要体现在几个方面。第一是货品的生命周期非常短。一箱普通工业品放半年没问题但一批叶菜可能三天就黄了一批冷冻禽肉可能只有几个月的保质期。这意味着系统不能只记录“库存数量”还必须记录“生产日期”“保质期”“入库批次”并且要有临期预警机制。第二是计量单位混乱。批发市场里土豆可以论斤卖也可以论袋卖鸡蛋论箱冷冻虾论件。如果不做单位换算库存账目会乱成一团。通用WMS里常见的“基础单位转换率”模型在这里是刚需。第三是损耗和报损是常态。运输碰伤、存放过久、天气变化都可能导致产品不能按原价出售。系统需要支持报损单、报溢单、盘点差异调整否则账面库存永远对不上实际库存。第四是批次溯源和先进先出。农牧产品批发商最怕什么怕的是先入库的货还在库房里积压后入库的货反而先卖出去了最后旧货过期砸手里。所以出库逻辑必须遵循先进先出原则而这一切都要落在批次管理上。我见过不少毕设代码库存表就一张字段就“产品id、数量、单价”三个完全没有批次概念。这种设计在答辩时一旦被问到“你们怎么做先进先出”“怎么追溯某一批产品的来源”基本就是当场暴露。所以说面向农牧产品批发商的仓储系统真正的难点不在CRUD而在于领域的特殊规则。1.1 业务边界与角色定位批发商到底需要什么功能在动手写代码之前先把业务边界画清楚。农牧产品批发商的典型业务场景是这样的从上游农户或者养殖基地采购农产品运到自己的仓库或者档口然后向下游的餐饮企业、零售商、社区团购团长批发销售。整个过程涉及采购入库、库存管理、销售出库、库存盘点、报损报溢以及围绕这些业务的供应商和客户管理。基于这个场景系统角色通常拆成三种管理员、仓库管理员仓管员、老板或者经理。管理员负责系统配置、用户管理、基础数据维护仓管员负责入库单、出库单、盘点单的实际操作老板关注库存总额、临期预警、滞销产品统计这些决策类数据。这里有个很多毕设容易犯的错角色划分过于简单只有“管理员”和“普通用户”两种。老师问“仓管员能不能看到采购价格”的时候你没有办法回答。合理的做法是至少区分出管理员、仓库主管、普通操作员三个层级用RBAC基于角色的权限控制模型来控制菜单和操作按钮的可见性。这个设计不仅专业也方便你在论文里多写一章“系统安全设计”。1.2 功能模块地图从入库到出库的完整闭环一个完整的农牧产品仓储管理系统核心功能模块应该包含这些基础数据管理产品分类、产品档案含单位、保质期默认值、供应商管理、客户管理、仓库库位管理采购入库管理采购订单创建、入库验收、入库单审核、自动更新库存销售出库管理销售订单创建、出库拣货、出库单审核、先进先出扣减库存库存管理库存查询、库存流水、库存盘点、报损报溢、库存预警批次与保质期管理批次追溯、临期预警、过期产品统计统计报表库存周转、采购汇总、销售汇总、损耗统计系统管理用户管理、角色权限、操作日志你可以把这些功能当成论文目录的雏形。每一块对应一个章节每一块都要写清楚业务逻辑、表结构、接口设计和页面展示。整个系统的核心逻辑线是“采购—入库—库存—出库—统计”其他所有功能都是围绕这条线的支撑。2. 技术方案选型Spring Boot Vue前后端分离的落地细节既然题目里明确写了springboot那技术栈的主线就很清晰后端用Spring Boot前端搭配Vue做前后端分离。这个组合是当前Java毕设的绝对主流好处有两个一是Spring Boot极大简化了项目的配置和部署你不需要像学习SSM时那样写一堆XML二是前后端分离的结构对应届生找工作面试时也更有话讲。具体的技术选型我建议这样定后端框架Spring Boot 2.7.x这个版本很关键后面会说为什么持久层框架MyBatis-Plus比纯MyBatis少写大量SQL适合快速开发数据库MySQL 5.7或8.0权限认证JWT无状态登录前后端分离的标准方案缓存Spring Data Redis用于验证码、热点数据缓存前端框架Vue 2.x Element UI成熟稳定、资料多遇到问题容易查或Vue 3 Element Plus如果时间充裕构建工具Maven后端、npm前端这里要特别提醒一下Spring Boot版本的问题。建议不要一上来就用Spring Boot 3.x。Spring Boot 3要求JDK 17及以上很多学校的实验环境装的是JDK 8而且网上大部分教程、踩坑记录都是基于Spring Boot 2.x的。你如果用了3.x遇到问题搜解决方案时会发现很多代码根本对不上。Spring Boot 2.7.x JDK 8 MyBatis-Plus 3.5.x的组合是当前稳到不能再稳的选择。2.1 项目目录结构与代码分层前后端分离的项目目录要清晰答辩时老师如果让你展示项目结构乱糟糟的会非常减分。后端我用标准的四层结构com.example.agriculture ├── controller // 控制层接收请求、返回结果 ├── service // 业务层处理业务逻辑 │ └── impl // 业务实现类 ├── mapper // 数据访问层MyBatis-Plus的Mapper接口 ├── entity // 实体类对应数据库表 ├── dto // 数据传输对象 ├── vo // 视图对象返回给前端的数据 ├── config // 配置类跨域、拦截器、Redis配置等 ├── common // 通用类统一返回结果、异常处理、常量 └── utils // 工具类JWT工具、日期工具等一个很重要的习惯是Controller里只做参数接收和结果返回不写业务代码。业务逻辑全部放到Service层。举个简单的例子入库单审核这个操作表面上只是把入库单的状态改成“已审核”但实际上它要经历校验单据数据——检查产品是否存在——更新库存表——写入库存流水——记录操作日志。这一串逻辑如果写在Controller里几百行代码堆在一起既难维护又难答辩。2.2 统一返回结果与全局异常处理很多毕设项目的接口返回值格式五花八门有的返回Map有的返回JSONObject有的直接返回字符串前端解析的时候写一堆if-else非常痛苦。从一开始就应该统一返回结果对象。我在项目的common包里定义了一个R类Result的简称结构大概像这样public class RT { private Integer code; // 状态码200表示成功500表示失败 private String message; // 提示信息 private T data; // 数据 public static T RT ok() { ... } public static T RT ok(T data) { ... } public static T RT fail(String message) { ... } }然后配一个全局异常处理器用Spring Boot的RestControllerAdvice注解捕获业务异常、参数校验异常、数据库异常等等统一包装成R对象返回。这样整个项目的接口风格高度一致前端Axios拦截器只需要判断code是不是200就能决定是否弹出错误提示。这套东西写起来不难但很多人会忽略。你在论文的“系统实现”章节里写上一段“本系统设计了统一的返回结果类和全局异常处理机制”技术含量一下就上去了。2.3 JWT登录认证与权限控制农牧产品仓储系统涉及库存和价格数据不能谁进来都能看所以登录认证必须做。Spring Boot整合JWT的标准流程是这样的用户登录成功后后端根据用户ID和角色生成一个JWT Token返回给前端前端把Token存在localStorage里每次发请求时在请求头里带上Authorization: Bearer token后端写一个拦截器拦截所有需要认证的请求校验Token的有效性然后解析出当前用户信息放入请求上下文。这里有一个容易踩的坑拦截器只校验了Token是否有效但没有校验用户的角色权限。比如一个普通操作员直接调用“删除所有库存记录”的接口地址理论上他也能成功。所以权限控制不能只靠拦截器还要在Service层或Controller层加角色判断。最简单的做法是用Spring的PreAuthorize(hasRole(ADMIN))注解但需要在启动类加EnableGlobalMethodSecurity(prePostEnabled true)Spring Boot 2.7写法如果是Spring Boot 3注解名变成EnableMethodSecurity。这一块做好了论文里的“权限管理设计”一章就有了核心素材。3. 数据库设计实战农牧产品场景下的表结构拆解数据库设计是仓储系统的灵魂也是毕设论文里最能展示专业度的地方。我之前帮一个学弟审核过代码他的数据库只有四张表用户表、商品表、入库表、出库表。这种设计只能说“能用”但离“合理”差得很远。我这里给你一套完整的表设计思路你可以根据自己的需求调整字段。核心表大概有这些用户表、角色表、产品分类表、产品档案表、供应商表、客户表、仓库表、库位表、采购单表、采购单明细表、入库单表、入库单明细表、销售单表、销售单明细表、出库单表、出库单明细表、库存表、库存流水表、盘点单表、盘点单明细表、报损报溢单表、批次表。表面上表很多但实际上很多是“主表明细表”的配对关系。比如采购单主表记录单号、供应商、采购日期、总金额、状态采购单明细表记录每个产品的数量、单价、生产日期、保质期。主表是“一次采购”的抬头信息明细表是“这次采购买了哪些东西”。3.1 产品档案表的设计保质期和计量单位是重中之重产品档案表是整个系统的基础设计得好不好直接影响后面的所有业务。我给你列一下关键字段product_id主键自增category_id分类ID关联产品分类表比如蔬菜、水果、肉禽蛋、水产、粮油product_name产品名称比如“土豆”、“冷冻鸡翅”specification规格描述比如“30斤/袋”base_unit基础单位比如“斤”purchase_unit采购单位比如“袋”sale_unit销售单位比如“斤”conversion_rate采购单位与基础单位的换算率比如1袋30斤default_shelf_life默认保质期天数比如肉类冷冻品180天、绿叶菜3天low_stock_threshold库存预警下限storage_condition存储条件要求比如“0-4℃冷藏”、“-18℃冷冻”status启用状态0停用1启用这个表里最容易被忽略的是default_shelf_life。为什么要冗余这个字段因为很多产品在录入采购单的时候默认保质期就是一样的比如你每次采购冷冻鸡翅都是180天保质期。如果每次都要手动输入效率低还容易出错。有了默认值入库人员不需要每次都填系统自动带出还可以按实际情况修改。3.2 库存表与批次表用批次串起产品生命周期库存表是整个系统的实时库存账。它的核心字段是stock_id主键product_id产品IDbatch_id批次ID这个是关键warehouse_id仓库IDquantity当前数量这里有个设计细节库存数量用基础单位的主单位数量还是用实际单位我的建议是库存表统一存基础单位的数量比如产品“土豆”的基础单位是“斤”采购单里录入的数量是“袋”系统自动转换成“斤”存入库存available_quantity可用数量如果有预订冻结的概念这个字段才有意义如果做简单版本可以不拆production_date生产日期expiry_date保质期到期日supplier_id供应商批次的概念是整个设计的核心。每一批产品入库时系统根据“产品ID 生产日期 供应商 入库时间”生成一个唯一的批次号。这样做的好处有两个第一出库时可以实现先进先出。同一产品有多批次库存出库时按expiry_date升序或者production_date升序排列先到期先出。第二可以实现追溯。发现某批次产品有质量问题可以直接通过批次号查出这批货是什么时候从哪个供应商采购的卖给哪些客户了。批次表的字段可以这样设计batch_id批次ID建议用年月日序号比如“20250615001”product_id产品IDsupplier_id供应商IDproduction_date生产日期expiry_date保质期到期日purchase_order_no关联采购单号initial_quantity初始数量remaining_quantity剩余数量status批次状态在库、已出清、部分出库3.3 库存流水表每一笔数量变化都要留痕库存流水表可能是整张表设计里最“专业”的一个点。它的作用是把所有导致库存数量变化的操作记录下来形成一份完整的操作日志。字段包括flow_id主键product_id产品IDbatch_id批次IDchange_type变动类型1采购入库、2销售出库、3报损、4报溢、5盘盈、6盘亏、7库存调整change_quantity变动数量正数入库、负数出库before_quantity变动前数量after_quantity变动后数量reference_no关联单号如入库单号、出库单号、盘点单号operator_id操作人IDcreate_time操作时间为什么要建这张表因为库存表的字段是“当前值”它是瞬时状态不保存历史。你没办法回答“上周三这批土豆是怎么从1000斤变成800斤的”这个问题。有了库存流水表每一次数量变动都清清楚楚这不仅是业务需要也是论文里“系统可靠性”“操作可追溯性”章节的重要素材。4. 核心功能实现入库、出库、盘点、预警的完整逻辑数据库设计好了接下来的核心就是业务逻辑的实现。这一部分我挑四个最关键的环节来讲你把这几块吃透了整个系统的主体功能就有了。4.1 入库流程采购入库与验收一次业务的完整闭环入库单的流程建议设计成“草稿—待审核—已审核—已入库”四个状态。这里为什么要用状态机因为在实际业务里仓管员录完入库单之后通常需要主管审核确认防止录入错误。虽然你的毕设可能只是演示但把这个流程做出来会显得业务理解到位。入库操作的业务逻辑可以用这样的伪代码来描述public void approveInboundOrder(Long orderId) { // 1. 查询入库单及明细列表 InboundOrder order inboundOrderMapper.selectById(orderId); ListInboundOrderDetail details inboundOrderDetailMapper.selectByOrderId(orderId); // 2. 校验订单状态 if (!DRAFT.equals(order.getStatus())) { throw new BizException(当前状态不允许审核); } // 3. 遍历明细逐项更新库存 for (InboundOrderDetail detail : details) { // 3.1 判断该产品是否有未过期的同批次库存 // 3.2 有则累加数量没有则新建批次 Stock stock stockMapper.findByProductIdAndBatchId( detail.getProductId(), detail.getBatchId()); if (stock null) { stock new Stock(); // 设置仓库、批次、数量、生产日期、保质期等信息 stockMapper.insert(stock); } else { // 更新数量、可用数量 } // 4. 写入库存流水 StockFlow flow new StockFlow(); flow.setChangeType(PURCHASE_IN); flow.setChangeQuantity(detail.getQuantity()); // ... stockFlowMapper.insert(flow); } // 5. 更新入库单状态 order.setStatus(APPROVED); inboundOrderMapper.updateById(order); // 6. 记录操作日志 }这一段代码的逻辑不复杂关键是要避免一个典型错误更新库存之前没有用乐观锁或行锁控制并发。两个人同时给同一产品入库理论上可能会把库存数量覆盖掉。简单的方法是在stock表加一个version字段更新时用UPDATE ... SET quantity quantity #{number}, version version 1 WHERE stock_id #{id} AND version #{version}。这个细节写进论文比长篇大论的“高并发设计”有说服力多。4.2 出库流程如何用代码实现“先进先出”出库是仓储系统里最容易出错的地方。农牧产品有保质期如果出库时随意扣减库存很可能把新到的货先卖了旧货全积压在库里。所以出库的核心逻辑是按批次先进先出FIFO。实现思路是这样的删除销售出库单时从前端传来产品ID和出库数量。后端拿到数量之后先查出该产品所有“有库存且在库”的批次按保质期到期日正序排列然后从最早到期的批次开始扣减。同样用一段伪代码表示public void deductStockByFifo(Long productId, BigDecimal quantity) { // 1. 查询所有有库存的批次按到期日升序 ListStock stockList stockMapper.findAvailableStockByProductId(productId); BigDecimal remainingQty quantity; for (Stock stock : stockList) { if (remainingQty.compareTo(BigDecimal.ZERO) 0) { break; } // 2. 如果当前批次库存足够 if (stock.getQuantity().compareTo(remainingQty) 0) { stock.setQuantity(stock.getQuantity().subtract(remainingQty)); stockMapper.updateById(stock); // 记录流水... remainingQty BigDecimal.ZERO; } else { // 3. 当前批次不足全部扣完继续下一个批次 remainingQty remainingQty.subtract(stock.getQuantity()); stock.setQuantity(BigDecimal.ZERO); stockMapper.updateById(stock); // 更新批次状态为已出清... } } if (remainingQty.compareTo(BigDecimal.ZERO) 0) { throw new BizException(库存不足); } }这里有几个细节值得注意。第一金额计算不能用double必须用BigDecimal。农产品的单价有三位小数、四位小数很常见double精度丢失会搞得账目不平。第二出库单价到底取采购价还是销售价简单做法是出库单价就是客户实际成交的销售价从销售单明细带过来库存流水记录的是数量变动不用纠结价格口径。第三先进先出的排序字段用expiry_date比用production_date更合理因为同一天生产的冷冻食品可能一个批次的保质期是180天另一个是210天按到期日排序才是真正的防范临期。4.3 保质期预警定时任务跟查询配合双保险农产品的保质期预警是必做功能。我这里实现了两层第一层是定时任务。Spring Boot里用Scheduled注解就可以每天凌晨扫描一次所有批次把7天内到期的产品标记为“临期”把已经过期的产品标记为“过期”。代码很简单Component public class ExpiryCheckTask { Scheduled(cron 0 0 2 * * ?) // 每天凌晨2点执行 public void checkExpiry() { // 查询所有剩余数量大于0的批次 // 计算到期日与今天的天数差 // 小于7天更新临期提醒 // 小于0天更新过期状态 } }第二层是查询时实时计算。我习惯在库存查询的列表页加一个“临期状态”列后端查询时根据expiry_date动态计算3天内“紧急”、7天内“警示”、已过期“过期”。这样即使定时任务没跑比如演示的时候页面上也能直观看到哪些货快到期了。临期预警在论文里非常有讲头你可以当成一个独立章节写从业务痛点分析农产品易腐、损耗率大到技术实现定时任务查询计算再到业务价值减少损耗、提高资金周转一气呵成。4.4 盘点与报损报溢让账面库存跟实际库存永远对得上做过仓库的人都知道库存账和实际货品数量永远有差异。原因可能是称重误差、搬运损耗、标签脱落、人为操作失误。所以盘点功能是仓储系统的必备能力。盘点的业务流是这样创建盘点单——指定盘点仓库或盘点产品范围——录入实际盘点的数量——系统自动对比账面数量和实盘数量——生成盘盈或盘亏差异——审核确认——自动调整库存并写入流水。实现的关键点有两个。第一盘点单审核之前不能直接改库存必须等确认之后一次性调整。这跟入库单的“先审核后入库”是一个思路。第二调整库存时会生成一条盘点调整类型的库存流水并且会把原库存数量、盘点数量、差异数量都记录清楚。这样即使盘点错了也能顺着流水查出原始记录。报损报溢单相对简单一些可以理解成“手动调整库存专用单”。仓管员发现一批水果烂了一部分录入报损单填写产品、批次、报损数量、报损原因碰撞、过期、霉变提交审核后系统扣减对应库存。5. 前后端实现要点Vue页面、接口对接与常见坑后端逻辑搞清楚了前端页面的设计也不能拉胯。仓储系统是典型的“表单密集型”管理系统页面不需要花里胡哨但操作流程要顺。前端我用Vue配合Element UI组件库来做。核心页面就几块登录页、首页数据看板、基础数据管理页产品、供应商、客户、仓库、入库管理页采购单列表、入库单列表、新增/编辑表单、出库管理页、库存查询页、盘点管理页、报表统计页。5.1 关键页面与接口设计建议库存查询页是整个系统的门面也是老师打开项目之后第一个会看的页面。我把这个页面的功能定为支持按产品名称、分类、仓库、临期状态筛选列表展示产品名称、规格、单位、总库存数量、批次数量、最早到期日、临期状态、库存预警状态每条记录可以点击查看“批次明细”。接口设计上就是这样GET /api/stock/list?page1limit10productNamecategoryIdwarehouseIdexpiryStatus GET /api/stock/batchList?productId123 GET /api/stock/flowList?productId123batchId456入库单页面我做成“新建→提交审核→审核列表”的流程。新建入库单的表单里有一个很实用的交互选择产品后自动带出该产品的默认保质期、单位换算率、上次采购价。这不是复杂技术但对于“好用”的感知非常明显。首页数据看板用ECharts画几张图比如本周入库/出库趋势、库存分类占比、临期产品Top10、供应商采购金额排行。这些图表直接从统计接口拿数据就行不需要定时任务去存聚合表。5.2 跨域处理与Axios封装前后端分离开发时跨域问题是必踩的坑。前端在8080端口跑Vue后端在8081端口跑Spring Boot前端请求后端必然跨域。最简单的解决办法是后端写一个配置类Configuration public class CorsConfig { Bean public CorsFilter corsFilter() { CorsConfiguration config new CorsConfiguration(); config.addAllowedOriginPattern(*); config.addAllowedHeader(*); config.addAllowedMethod(*); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, config); return new CorsFilter(source); } }然后再把拦截器里Token校验的接口放行做好。注意addAllowedOriginPattern(*)和setAllowCredentials(true)要一起用如果只写addAllowedOrigin(*)会有兼容性问题。前端我用Axios封装了一个request对象统一做了三件事请求拦截器自动加Token响应拦截器判断code200则返回data非200则用Element UI的Message弹出错误遇到401Token失效则跳回登录页。这套封装是每一届前端开发的标配动作但确实能省掉无数重复代码。5.3 图表统计接口的SQL写法统计功能要用到聚合查询举两个实际例子。本周出入库趋势SELECT DATE(create_time) AS d, SUM(CASE WHEN change_type IN (PURCHASE_IN, OTHER_IN) THEN change_quantity ELSE 0 END) AS in_quantity, SUM(CASE WHEN change_type IN (SALE_OUT, SCRAP_OUT) THEN ABS(change_quantity) ELSE 0 END) AS out_quantity FROM stock_flow WHERE create_time DATE_SUB(CURDATE(), INTERVAL 7 DAY) GROUP BY DATE(create_time) ORDER BY d临期产品Top10SELECT p.product_name, s.batch_id, s.expiry_date, DATEDIFF(s.expiry_date, CURDATE()) AS days_to_expiry, s.quantity FROM stock s LEFT JOIN product p ON s.product_id p.product_id WHERE s.quantity 0 AND s.expiry_date IS NOT NULL ORDER BY s.expiry_date ASC LIMIT 10写这类SQL的时候注意一点库存流水的change_quantity不要全存正数入库存正数、出库存负数统计时用ABS或者CASE WHEN处理会灵活很多。我见过有人为了图简单入库存正数出库也存正数再用一个字段标记方向后来查流水账的时候各种别扭。6. 测试与部署把自己的代码跑稳别在答辩时翻车答辩现场最尴尬的事情就是老师让你打开系统结果项目启动报错。我见过太多这样的案例了基本都是环境不一致或者依赖版本冲突造成的。这里给几个让系统稳定跑的实操建议。后端环境锁死在Spring Boot 2.7.18版本。这个版本是Spring Boot 2.x系列的最后一个维护版本稳定性极高网上的资料也最全。它搭配的MyBatis-Plus版本建议用3.5.3.1别用太新的3.5.5以上版本因为插件机制有调整部分旧教程里的分页配置写法会失效。MyBatis-Plus的分页插件配置是一个经典的坑忘了配置分页插件调用selectPage会发现根本没分页。正确配置是这样Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }前端部署更简单npm run build之后把dist目录里的静态文件交给Nginx托管就行。如果只需要本地演示可以直接用npm run serve让前端跑在开发模式后端跑在Spring Boot内置的Tomcat两个服务同时开就不用折腾Nginx了。但你要在论文里写“基于Nginx实现前端静态资源部署”把Nginx配一下也不是难事。测试方面不要只测正常流程。入库单审核之前重复提交审核会不会把库存加两次同一批次重复入库库存是累加还是覆盖库存不足时出库能否被正确拦截这些边界情况是老师在演示时最喜欢动手点的地方。7. 常见问题排查我开发过程中踩过最深的几个坑最后分享一下我在开发和帮别人改这个项目过程中遇到频率最高的几个问题。每一个都是真实踩过的坑写出来帮你省几天时间。第一IDEA里的Spring Boot启动类报“Cannot determine embedded database driver class”。这个一般是数据库连接配置没生效检查application.yml里的spring.datasource.url格式对不对spring: datasource: url: jdbc:mysql://localhost:3306/agriculture_stock?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver注意serverTimezoneAsia/Shanghai必须加不加MySQL 8.0下面时间会报错。驱动类名在MySQL 8.x下必须是com.mysql.cj.jdbc.Driver不是com.mysql.jdbc.Driver。第二前端请求后端404但接口地址看起来是对的。先查两个地方后端有没有配RequestMapping的context-path比如有的人配置了server.servlet.context-path/api那前端请求的路径就得带上前端Axios的baseURL和后端实际端口一不一致。这个问题折磨了我整整一天最后发现是端口写错了。第三JWT拦截器放行配置没写对导致登录接口自己也报401。拦截器里通常要对登录接口、验证码接口、静态资源做excludePathPatterns。我之前漏了登录接口结果前端没法登录排查了很久才发现是拦截器把自己人拦了registry.addInterceptor(jwtInterceptor) .addPathPatterns(/**) .excludePathPatterns(/login, /captcha, /error, /doc.html, /webjars/**, /favicon.ico);第四MyBatis-Plus自动填充时间字段失效。如果你用了TableField(fill FieldFill.INSERT)这类注解做创建时间自动填充需要写一个MetaObjectHandler的实现类。很多人只加注解不写处理器结果插入数据时create_time是null。第五前端上传大文件或者表单提交慢。多半不是代码问题是Spring Boot默认的单次请求大小限制太小了。不过仓储系统主要是录单、开单这类表单操作很少涉及大文件上传遇到这个问题的概率不大。如果你想上传产品图片把限制调大一点就行。8. 从开发到答辩论文结构、亮点包装和自我提升代码写完了只是第一步毕设的灵魂在论文和答辩。我见过太多代码质量还不错、但因为不会表达而拿不到高分的同学。这里给你一个论文结构的参考和答辩的技巧让你的项目价值被看到。论文结构可以这样安排第一章绪论写背景和意义不要只说“随着信息技术的飞速发展”要落到具体场景农牧产品批发市场是农产品流通的关键环节面临着保质期短、损耗率高、库存管理粗放等痛点因此设计一个面向批发商仓储管理系统具有重要意义。第二章相关技术介绍写Spring Boot、Vue、MyBatis-Plus、MySQL、Redis、JWT每样写清楚“是什么为什么选它”不要写成API文档。第三章需求分析画出系统的功能模块图、角色用例图列出功能性需求和非功能性需求系统响应时间、安全性、可用性等。第四章系统设计重点写数据库设计放E-R图和核心表结构把产品批次、保质期预警、先进先出这些设计的“为什么”写透。第五章系统实现这是最占篇幅的可以按模块截图配核心代码库房人员、入库、出库、盘点、预警、报表每个模块写“页面展示功能描述关键代码实现效果”。第六章系统测试写测试用例、测试结果展示系统功能和性能测试结果别人写测试是走个形式你最好真的测并且把测试用例表做得细致一点。第七章总结与展望总结做了什么、有哪些不足、后续可以怎么完善这里忌讳空话套话要说实话。答辩时最容易加分的地方是“特殊业务场景的处理”。你可以主动讲这三个点一是产品保质期的分级预警机制。仓库里有保质期只有几天的叶菜也有保存半年的冻肉一个简单的“临期7天提醒”满足不了需求。我设计了“紧急3天内、警示7天内、过期”三档不同分类可以自定义预警天数比如叶菜类预警1天冻品类预警90天。二是先进先出的底层逻辑。不是简单按入库时间排序而是按到期的先后顺序出库防止临期产品积压。三是单位换算。采购用袋、库存用斤、销售可能用箱中间通过换算率自动转换避免人工换算出错。这三个点一说出来老师立刻就知道你不是在写一个“复刻版进销存”而是真的调研过农牧产品批发商的业务。整篇博文写到这里核心的东西已经全部摊开。最后分享一个我在实际操作中的体会做这种毕业设计最大的难点往往不是Spring Boot的语法、Vue的组件调用这些技术细节而是你能不能从“把功能做出来”进一步提升到“把业务想明白”。农牧产品的批次、保质期、单位换算、先进先出、损耗管理这些才是这个项目的价值所在。把这一层想通了代码模块自然就清晰了论文也写得出来。如果你正准备开始做不用一上来就闷头写代码先拿一张纸把这个系统的业务闭环画出来你后面会感谢自己这个习惯。
返回列表