每到毕业设计季我后台总能收到一堆高相似度的问题SpringBoot 和 Vue 到底怎么连起来物资管理系统的数据库表要建几张才够论文里的测试数据怎么造才不像编的部署文档写到什么程度才算完整这些问题背后指向的通常都是同一个热门选题——抗疫物资管理系统。说它热门是因为它既蹭上了公共事件的管理需求又有典型的管理信息系统特征做起来工作量适中论文理论也不缺整体是性价比很高的一道题。但正因为它看起来简单大部分学生做出来的版本都停留在能登录、能增删改查的水平答辩时被评委几个问题一问就卡壳。这篇内容我会把这个项目从需求拆解、数据库设计、后端实现、前端页面、部署上线到论文答辩的完整链路过一遍。适合正在做这个选题、或者打算拿它当参考的读者也适合那些想从能用进阶到能讲清楚的同学。你不需要完全照抄我的方案重点是理解每一步选择背后的原因——这才是毕业设计真正考核的东西。1. 选题拆解抗疫物资管理系统到底在管什么1.1 需求拆解先别急着写代码把场景想清楚抗疫物资管理系统听着很宏大但你把它拆开看本质就是一套针对应急场景的仓库管理系统。口罩、消毒液、防护服、测温枪、隔离衣这些物资在紧急状态下有几个特点出入库频率高、发放对象多、部分物资有批次和有效期要求、库存一旦见底直接影响使用。所以它和普通进销存系统的核心区别不只是管库存而是要管得更及时、更精细、更可追溯。我把这套系统常见的核心业务场景列一下你可以拿自己的需求文档逐项对照基础数据管理物资分类、物资档案、供应商、部门与用户信息。入库管理采购入库、捐赠入库要记录来源类型、批次号、生产日期、有效期、数量和经手人。出库管理部门领用、定向发放要记录领取人、领取部门、用途和数量。库存台账实时汇总每种物资的当前库存并能按分类、按时间范围查询。预警管理库存低于下限、物资临近有效期时给出醒目提醒。统计报表出入库趋势、物资类别占比、高频发放物资排行。系统管理用户角色权限、操作日志、密码修改。如果你的选题描述里带平台两个字那通常还要多一条领用申请审批流程普通用户提交申请单仓库管理员审批通过后自动完成出库拒绝则申请单走到作废状态。这一条流程是整个项目最值钱的部分它把物资管理从纯台账升级成了流程化管理论文里的用例图、时序图、状态流转图全都有的画答辩素材一下就丰富了。我见过不少同学一上来就建一张物资表加一张出入库表开写结果做到一半发现领用申请没有单据状态、出库记录和库存对不上、同一物资的不同批次无法区分。这些问题本质都是需求阶段没拆干净导致的返工所以第一节先别碰代码把场景理顺比什么都重要。1.2 为什么 SpringBoot Vue MySQL 是毕业设计的安全组合选技术栈这件事原则不是越新越好也不是越复杂越好而是完整、合理、能讲明白。SpringBoot Vue MySQL 这套组合能成为毕业设计绝对主流原因很朴素。第一SpringBoot 把 SpringMVC、事务、连接池、JSON 序列化这些原本要写一堆配置的东西全部收敛成 starter 依赖一个带 main 方法的工程就能直接跑起来学生不容易在环境配置上耗光耐心。第二Vue 的组件化开发配合 Element UI 这类现成组件库列表、表单、弹窗、分页全是成熟方案前端出效果的速度非常快统计图表接上 ECharts 就能可视化。第三MySQL 是高校教学里普及度最高的数据库建表、索引、事务、视图这些概念在论文理论章节里都有的写。对比一下其他路线你就明白它为什么是安全牌。用 JSP Servlet 做传统单体工作量大了不少界面观感还停留在十年前用 Python 写后端技术本身没有问题但论文里主流企业级技术这个描述就不如 SpringBoot 硬气一上来就搞微服务拆分、注册中心、分布式事务概念一旦展开答辩时很容易被问穿。所以除非导师有明确偏好否则这个组合确实是最稳妥的选择这也是大量课程设计模板都在用的原因。但安全不等于随便。同一个技术栈有人做出来像课程作业有人做出来像可交付的产品差距就藏在数据库设计和业务闭环里。1.3 角色与权限三套视角决定功能边界权限模型不复杂物资管理系统不需要多租户和细粒度权限引擎一套简单的 RBAC基于角色的访问控制完全够用。我这里推荐划分三种角色每个角色的功能边界必须在动工前先画清楚角色核心功能说明系统管理员用户管理、物资分类维护、供应商管理、操作日志查看、预警阈值配置负责基础数据和系统运维一般不直接经办出入库仓库管理员入库登记、出库登记、库存调整、领用申请审批、台账与报表查看系统的核心业务操作者普通用户查看库存与物资、提交领用申请、查看本人申请记录与审批状态只能操作与自身相关的数据角色定下来之后后端每个接口都要做角色校验前端菜单按角色渲染这样越权访问这个基础安全问题就解决了论文里也可以正经写一节基于 RBAC 的权限控制设计。2. 数据库设计表结构、并发扣减与初始化脚本里的细节2.1 核心表结构与字段设计思路数据库是答辩时最容易暴露问题的地方。代码你可以背熟但表结构和字段逻辑是纸面硬货评委一句为什么这张表冗余这个字段你就得当场解释设计初衷。我的建议是在表数量上不要省事宁可多拆两张表也别在一张表里堆一切。下面是这套系统常用的核心表清单你可以按需增删表名职责关键字段user用户与角色id, username, password, real_name, role, phone, dept_id, statusmaterial_category物资分类id, name, remarkmaterial物资档案与总库存id, category_id, name, spec, unit, supplier_id, stock, low_limit, high_limitmaterial_batch物资批次库存id, material_id, batch_no, production_date, expire_date, remaining_quantity, statusstock_in_record入库流水id, material_id, batch_id, quantity, source_type, source_name, operator, in_time, remarkstock_out_record出库流水id, material_id, batch_id, quantity, receiver_name, receiver_dept, purpose, operator, out_timerequisition领用申请单id, applicant_id, material_id, batch_id, quantity, reason, status, approver_id, apply_time, audit_timeoperation_log操作日志id, username, module, action, ip, create_time这里有几个设计点需要特别解释。第一个是批次表。口罩、防护服这类物资有生产日期和有效期如果只有一张总库存表你永远说不清快过期的到底还剩多少。material_batch 表单独记录每个批次的剩余量出库时按先到期先出的原则扣减过期批次可以标记停用。这张表是你和普通仓库管理系统拉开差距的关键也是论文里应急物资精细化管理的直接证据。第二个是总库存字段的处理。我见过两种做法一种是在 material 表上直接存 stock 总数量出入库时同步加减另一种是单独建库存汇总表。对课程设计来说直接在 material 表上加 stock 字段加一个更新时间戳就够了每次流水写入后同步更新。但注意这里的更新绝不能先查再减原因下一节专门讲。第三个是冗余字段要克制。比如 stock_in_record 里已经有 material_id就不要再把 material_name、category_name 也塞进去查询展示时用 JOIN 关联即可。过度冗余会让论文里的 E-R 图很难画也容易在答辩时被问出逻辑矛盾。2.2 库存扣减与并发控制的正确姿势这是典型的看起来简单、一问就露馅的问题。不少同学的出库逻辑长这样先 SELECT 出当前库存判断够不够够就 UPDATE 减掉。这套流程在演示时完全正常但只要两个管理员同时操作或者多个领用申请审批同时触发自动出库就可能出现查的时候够、减的时候库存已经被别人扣走的竞态也就是所谓的超卖。正确的课程设计级方案是条件更新 影响行数判断核心 SQL 要这样写UPDATE material_batch SET remaining_quantity remaining_quantity - #{quantity} WHERE id #{batchId} AND remaining_quantity #{quantity} AND status 1对应的 Service 方法大致如下Transactional public void stockOut(StockOutRequest req) { // 批次维度扣减剩余量 扣减数量 才允许更新返回影响行数 int rows stockBatchMapper.deductQuantity(req.getBatchId(), req.getQuantity()); if (rows 0) { throw new BizException(库存不足或批次状态异常扣减失败); } // 写出入库流水 stockOutRecordMapper.insert(buildRecord(req)); // 同步更新物资总库存 materialMapper.decreaseStock(req.getMaterialId(), req.getQuantity()); }关键点在于 WHERE 里带着remaining_quantity #{quantity}。UPDATE 操作会锁住这一行并发情况下只有一个请求能匹配条件并返回影响行数 1其他的请求影响行数是 0直接抛库存不足提示。这套方案不需要 synchronized、也不需要引入 Redis 锁在单机部署下就能保证库存不超卖。答辩时如果评委追问多台服务器怎么办你再补一句可以通过 Redis 分布式锁或乐观锁版本号字段做扩展设计深度立刻就出来了。事务方面记得在 Service 方法上标注Transactional把批次扣减、流水写入、总库存更新包在同一个事务里保证要么全部成功、要么全部回滚。这个注解值得在论文里单独写一小节属于性价比极高的理论点。2.3 初始化 SQL 脚本中的常见坑数据库脚本看起来不起眼却是我检查项目时第一个打开的东西。以下几个坑务必避开建表 SQL 必须指定ENGINEInnoDB DEFAULT CHARSETutf8mb4。MyISAM 不支持事务utf8 不支持完整字符集到了录入特殊备注时会直接报错。外键约束在课程设计里可以不用但逻辑外键关联 id一定要完整自测保证 JOIN 出来的数据对得上。测试数据要像真的。不要全部是口罩 99999 个这种拍脑袋数字供应商、部门、批号、日期要有合理的离散度。后面统计图表好不好看全看测试数据有没有梯度。脚本命名要带执行顺序比如1_init_db.sql、2_init_data.sql让老师照着跑就能复现环境。3. 后端落地SpringBoot 分层、JWT 鉴权与物资流转闭环3.1 分包规范与统一响应机制后端部分的核心不是把功能写出来而是写得像工程。我建议的包结构是分层的com.example.ms ├── common // 统一结果封装、异常处理、常量、工具类 ├── config // WebMvc、拦截器、跨域配置 ├── controller // 接口层只做参数接收与响应 ├── service // 业务逻辑层事务边界控制在这里 ├── mapper // MyBatis 数据访问层 ├── entity // 数据库实体对象 └── dto // 请求参数与返回视图对象controller 里不写业务逻辑这是硬规矩。现在很多代码生成器能自动生成三层 CRUD但入库、出库这种多表联动的业务一定要在 service 层手写完整逻辑。统一响应建议用泛型ResultT包含 code、message、data 三个字段code 为 200 表示成功业务失败用 500 或自定义错误码。搭配全局异常处理器RestControllerAdviceDAO 层抛出的异常、业务校验失败的提示、参数绑定错误全部统一转成 JSON 返回前端只判断 code 就能稳定处理。跨域问题也得提前处理。开发时前端跑在 5173 或 9527 端口后端跑在 8080不配置跨域前端登录请求一上来就报 CORS 错误。最简单的做法是写一个 CorsConfig 放行开发环境生产环境用 Nginx 反向代理后其实不存在跨域这个后面部署章节会提到。3.2 登录鉴权JWT 方案与代码骨架现在毕业设计基本都用 JWTJSON Web Token做无状态登录而不是传统的 Session。原因很直接前后端分离后Token 由前端存在 localStorage请求时放进 Authorization 请求头后端校验之后从 Token 里读取用户身份服务端不存会话状态实现简单演绎逻辑也清晰。实现上分三步。登录成功后生成 TokenString token Jwts.builder() .setSubject(userId.toString()) .claim(username, user.getUsername()) .claim(role, user.getRole()) .setExpiration(new Date(System.currentTimeMillis() 7200_000L)) // 2小时 .signWith(key) .compact();后端拦截器或过滤器统一校验try { Claims claims Jwts.parserBuilder() .setSigningKey(key).build() .parseClaimsJws(token).getBody(); request.setAttribute(userId, claims.getSubject()); request.setAttribute(role, claims.get(role)); // 继续放行 } catch (Exception e) { // 直接返回 401 未授权 }之后在需要管理员权限的接口上做角色判断比如if (!0.equals(role) !1.equals(role)) { throw new BizException(无权限访问该接口); }密码存储不要用明文。MD5 加盐或者 BCrypt 哈希都可以写进论文BCrypt 的优势是自带盐且抗暴力破解缺点是你得用同一个工具类预先给测试数据生成好哈希串别在 SQL 脚本里怼明文。JWT 有三个细节想清楚再动手过期时间通常设 2 到 8 小时前端收到 401 后要自动清除 Token 并跳转登录页退出登录只需要丢弃前端 Token服务端无状态不需要额外处理。3.3 入库、出库、领用审批的完整业务闭环入库流程要求你把三步串在一个事务里选择物资 → 填写数量、来源、批次号、生产日期、有效期 → 写入 material_batch → 同步增加 material 总库存 → 写 stock_in_record 流水。三个步骤分开写中间任何一个环节失败都会造成库存和流水对不上所以事务边界一定要包住。出库分两种模式。直出模式仓库管理员直接选择物资和批次填领取部门和领取人系统校验批次剩余库存后扣减申请模式普通用户在领用申请模块发起单据填物资、数量和用途状态为待审批仓库管理员通过后系统自动完成批次扣减和流水写入拒绝则状态置为已拒绝。审批通过的后端逻辑大致是这样的骨架Transactional public void approveRequisition(Long requisitionId, boolean approved) { Requisition req requisitionMapper.selectById(requisitionId); if (req null || req.getStatus() ! 0) { throw new BizException(申请单不存在或已处理); } if (approved) { int rows stockBatchMapper.deductQuantity(req.getBatchId(), req.getQuantity()); if (rows 0) { throw new BizException(库存不足无法通过); } stockOutRecordMapper.insert(buildOutRecord(req)); materialMapper.decreaseStock(req.getMaterialId(), req.getQuantity()); req.setStatus(1); } else { req.setStatus(2); } requisitionMapper.updateById(req); }这里的状态判断加库存扣减加流水写入在一个事务里完成就算中途出现异常也能全部回滚不会出现申请单显示已通过但库存根本没扣的尴尬情况。4. 前端实现Vue 页面组织、Axios 封装与统计图表4.1 页面组织与路由设计前端我习惯用 Vue 加 Element UI如果你时间充裕Vue 3 加 Element Plus 也没什么问题但选哪套取决于你能找到的参考资料能不能闭环。页面组织直接按业务模块拆/login登录页/layout主框架左侧菜单、顶栏、内容区/dashboard工作台展示库存预警、今日出入库量、待审批申请数/material物资档案管理/stockIn 和 /stockOut入库登记、出库登记/requisition领用申请普通用户提交与审批管理员操作/report统计报表/system/user用户管理路由建议做成按角色动态生成的方案。普通用户登录后不应该看到用户管理菜单最简单的做法是登录时后端返回角色前端在路由守卫里根据角色过滤路由表。路由守卫里同时做一件事检查 localStorage 里有没有 Token没有就强制跳转登录页避免用户手动敲 URL 绕过登录。4.2 Axios 请求封装与统一错误处理所有接口请求不要散落在组件里必须统一封装。我的习惯是建一个src/utils/request.js基于 axios 实例做三件事配置基础 URL、请求拦截器自动携带 Token、响应拦截器统一处理业务码。const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) config.headers[Authorization] token return config }) service.interceptors.response.use( response { const res response.data if (res.code ! 200) { ElMessage.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) } return res.data }, error { if (error.response error.response.status 401) { localStorage.removeItem(token) router.push(/login) } ElMessage.error(网络异常请检查后端服务) return Promise.reject(error) } )封装完之后组件里调用就非常干净const list await getMaterialList(params)。联调时如果接口报错先看浏览器 Network 面板的请求地址、状态码和响应体再看后端控制台日志这是排查前后端问题最基础也最有效的一套流程。4.3 统计报表让数据可视化撑起门面统计页面是整个项目的视觉高光区也是论文截图里最能撑场面的部分。推荐三个基础图表近 30 天出入库趋势折线图。对应接口按天聚合出入库数量SQL 类似SELECT DATE_FORMAT(in_time, %Y-%m-%d) AS day, SUM(quantity) FROM stock_in_record GROUP BY day ORDER BY day。物资类别库存占比饼图。按分类统计各分类下的库存总数或库存金额。库存预警 TOP 10 条形图。查当前库存低于下限阈值的前十个物资。ECharts 的接入不复杂npm 安装 echarts页面里引入在mounted中初始化实例拿到后端聚合数据后调用setOption。有两个细节别忽略组件销毁时调用chart.dispose()避免切换页面后内存泄露或白屏给图表容器设置固定高度不要在数据回来之前渲染否则容器高度为 0图表画不出来。4.4 按钮级权限控制菜单权限用 v-if 判断角色就够了但按钮级权限建议封装一个自定义指令。比如只有仓库管理员能看到的出库登记按钮可以注册一个v-permission指令使用的时候写v-permission[1]指令内部比对当前用户角色没有权限的就把这个按钮元素移除。这个自定义指令在论文里也是一处可以写的技术亮点。5. 拉开差距的加分项库存预警、批次追溯与操作日志5.1 库存预警与工作台提醒基础版系统是你去看报表才知道库存不够加分系统是打开工作台就能看到哪些物资告急。库存预警的实现有两种思路一种是每次查询库存时实时计算是否低于 low_limit另一种是用 Spring Boot 的Scheduled定时任务每天扫描一遍。课程设计推荐第一种逻辑简单、无需考虑调度任务的执行时机前端工作台查出预警清单后直接用红色标签标出库存不足再在顶部菜单栏做一个待处理角标演示效果非常直观。5.2 批次管理与有效期提醒material_batch 表建好了就要把它的价值用起来。出库时按先到期先出的规则推荐批次比如有效期内剩余天数最少的批次排在出库列表第一位。过期批次的状态标记为停用不能再被选择出库也可以加一个近效期提醒当物资的有效期距离当前日期小于某个阈值比如 30 天时在列表里打上黄色临期标签。这两个功能在物资管理类系统里很能体现专业性而且在论文的功能实现章节里能写得很详细。5.3 操作日志基于 AOP 的轻量审计操作日志是很多课程设计忽略的部分但它恰恰是管理类系统的基本素质。实现方案用 AOP 切面加自定义注解定义一个Log(入库登记)注解打在这个方法上切面在方法执行后自动记录操作人、操作模块、IP 地址和时间。代码骨架大致是Aspect Component public class LogAspect { AfterReturning(annotation(log)) public void saveLog(JoinPoint joinPoint, Log log) { String username (String) SecurityUtils.getAttribute(username); String ip IpUtils.getIpAddr(); operationLogMapper.insert(new OperationLog(username, log.value(), ip, new Date())); } }日志不需要做到字节级审计只要让管理员能按时间和操作人查出谁在什么时候做了什么操作即可。这个模块答辩分量不大但体现的是工程意识和安全意识。6. 打包部署从本地 IDEA 到服务器上线的完整链路6.1 配置分离与本地打包很多人的代码在 IDEA 里跑得欢一打包部署就四处报错根本原因是配置没有与环境分离。我的做法是在 resources 下建两个配置文件application-dev.yml和application-prod.yml开发环境用本地 MySQL、本机端口生产环境用服务器地址。启动时通过--spring.profiles.activeprod指定使用哪个环境。后端打包用 Mavenmvn clean package -DskipTests打完的 jar 在target目录下。前端打包用 npmnpm run build生成的是dist静态目录。前后端分离项目最终交付物就是一个 jar 加一个 dist 文件夹部署逻辑非常清晰。6.2 Nginx 加 SpringBoot 的经典部署方案服务端部署我推荐最经典也最容易讲清楚的方案后端 jar 用nohup后台运行前端 dist 用 Nginx 托管Nginx 再把/api开头的请求反向代理到后端端口。这样前后端同一个域名访问不存在跨域问题也符合生产环境的一般实践。Nginx 关键配置如下server { listen 80; server_name _; root /opt/dist; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }后端启动命令nohup java -jar ms-system.jar --spring.profiles.activeprod app.log 21 部署完成后的验证顺序很重要先curl http://127.0.0.1:8080/api/health确认后端活着再访问服务器 IP 确认前端页面出来最后做一次完整的登录加查询。网络不通先排查防火墙和云服务器安全组是否放行了 80 端口端口占用用netstat -tlnp查这是最多人踩的坑。6.3 部署文档怎么写才算合格部署文档是交付物里常被低估的一项它直接决定老师能不能在你不在场的时候把你的系统跑起来。一份合格的部署文档至少要包含环境清单JDK、Maven、Node、MySQL、Nginx 的版本号别写最新版版本差异会带来一堆莫名其妙的问题。数据库部分建库建用户命令、SQL 脚本执行顺序、数据库连接账号密码说明。后端部分打包命令、jar 存放位置、启动命令、日志文件位置。前端部分构建命令、dist 上传路径、Nginx 配置内容、代理路径说明。常见问题表数据库连不上、端口被占用、页面白屏、登录 401每个问题配一句诊断思路。这份文档你自己部署时也要按着走一遍哪怕多花两个小时答辩时就是部署文档操作性强的直接证明。7. 论文与答辩测试证据、图表呈现与演示节奏7.1 论文章节与撰写顺序论文写作我有一个明显的建议先做系统再写论文但论文的核心章节要在编码阶段同步积累素材。最常见的章节结构是绪论背景、意义、国内外研究现状。相关技术介绍SpringBoot、Vue、MySQL、JWT。需求分析业务需求、功能需求、非功能需求配合用例图和用例描述表。系统设计总体架构图、功能模块设计、数据库设计E-R 图、数据字典。系统实现按模块贴截图和核心代码配合文字说明实现思路。系统测试测试环境、功能测试用例表、测试结果分析。总结与展望。撰写顺序上先写相关技术介绍这部分最费时间但最机械再写需求分析因为需求文档你动手前已经理清了然后是系统设计数据库表结构和 E-R 图直接用你建好的表最后补系统实现和系统测试。绪论和总结放在最后写因为只有系统做完了你才说得清楚价值和不足。7.2 测试用例与性能数据的组织测试章节是评委最爱翻也是最容易看出水分的部分。不要写系统功能正常测试通过这种一句话带过。至少准备十几条功能测试用例每条包含功能点、前置条件、操作步骤、预期结果、实际结果、是否通过。比如用例编号功能点操作步骤预期结果实际结果TC-001管理员登录输入正确账号密码点击登录登录成功并跳转工作台与预期一致通过TC-002普通用户越权访问通过 URL 直接访问用户管理接口返回无权限提示与预期一致通过TC-003库存不足出库对剩余 10 件的批次执行出库 20 件系统拒绝并提示库存不足与预期一致通过TC-004并发扣减两个请求同时扣减同一批次只有一个成功库存不为负与预期一致通过测试用例这里最怕的是预期结果含糊。用例要有足够的针对性特别是把并发扣减、越权访问、失效批次出库这类防护性场景写进去这会直接提升论文的专业分。性能测试可以用 JMeter 简单压一下在单机 4 核 8G 测试环境里对登录接口和库存查询接口分别施压 100 个并发记录平均响应时间和错误率一般登录接口平均响应在 300 到 500 毫秒、查询接口在 200 毫秒以内就足够交差。性能测试的数据不需要漂亮真实即可关键在于你能解释清楚测试环境、并发数、采样时长这几个要素。7.3 答辩演示的节奏与高频提问演示环节是答辩的重头戏很多同学栽在功能太多没有重点上。我建议设计一条 2 到 3 分钟的业务主线按场景讲故事而不是按菜单逐个点先用管理员账号登录展示工作台强调今日库存预警 3 项、待审批申请 5 条这些一眼可见的信息然后点进预警列表顺势演示补货入库强调入库后库存实时更新再切到普通用户视角提交一个领用申请切回管理员视角审批通过展示库存扣减和出库流水生成最后打开统计报表用折线图把刚才的出入库数据可视化出来。这样一条线走下来等于把系统的核心功能全部覆盖了一遍评委不需要猜你的设计意图。答辩高频问题提前准备一下答案这些是最常被问到的为什么选 JWT 而不是 Session答前后端分离无状态服务端不保存会话扩展性好。如何防止库存超卖答条件更新加影响行数判断配合事务保证一致性。MyBatis-Plus 和原生 MyBatis 有什么区别答MP 提供通用 CRUD简化开发自带的条件构造器避免拼接 SQL 的风险。如何防止 SQL 注入答基于预编译的占位符参数同时避免字符串拼接。如果要做多仓库版本该怎么改进答增加仓库表库存表和批次表都加上仓库维度出入库单据关联仓库字段。这些问题都不难但如果你没有提前整理过现场很容易被问住值得花半天时间把答案写熟。做了这么多类似的项目我最大的体会是毕业设计的评分高低不在于你的系统多了几个花哨接口而在于每块设计都有明确理由、每个结论都有测试证据。数据库为什么这样建、扣库存为什么这样写、权限为什么这样分这些逻辑链条理清楚了论文自然有内容答辩自然有底气。最后再分享一个小建议答辩前一定准备一份离线部署包把所有 jar、dist、SQL 脚本、部署文档打个压缩包带着现场演示环境一旦出问题这份包能让你在五分钟内重建系统从容程度完全不一样。