SpringBoot+Vue社区应急物资申报管理系统:全栈开发实战与设计解析 每年毕业季前后后台总能收到一批类似的私信能不能推荐一个Java全栈项目需要SpringBoot做后端、Vue做前端、MySQL存数据要完整源码最好是管理系统。说实话这类“一套代码覆盖主流技术栈”的项目需求一直很稳定因为它对应的是计算机相关专业里最常见的毕业设计、课程设计和简历项目。今天要聊的这个基于SpringBootVue的社区应急物资申报管理系统就是这类项目的典型代表后端用SpringBootMyBatis处理接口和数据的增删改查前端用Vue搭单页应用数据库用MySQL业务上围绕“居民在线申报、管理员审核发放、库存实时联动”这样一个完整闭环展开。它不算复杂但把全栈开发的重要环节都串起来了既能当毕设交差也能让你在面试里对“权限、事务、分页、接口设计”这些高频问题有话可说。这个系统的核心价值在于“申报-审批-出库”不是三个孤立动作而是同一个数据链条上的连续状态流转。物资表里的库存数字、申报记录里的审批状态、用户表里的角色权限任何一环出问题都会导致整个流程卡住。所以做这个项目的过程中你对“为什么状态要跟着数据走”“为什么扣库存要加事务”“为什么前端要拦路由”这些理念会理解得比看十篇教程都透彻。下面我按实际开发顺序把这个项目的设计思路、核心实现和踩坑过程完整拆一遍。1. 项目在解决什么问题直接看标题可能觉得就是个“管理系统”模板但把场景换成“社区物资保障”问题就具体得多了。社区工作人员在突发公共卫生事件或物资调拨期间需要快速统计居民需求、按优先级分配有限物资、留痕每一笔申领记录。线下用Excel和纸质登记表不是不行但一旦申报量上到几百上千条重复填写、版本冲突、统计滞后、审核无记录这些麻烦会一起涌上来。这个系统把它拆成了三个问题来解决。一是申报入口统一。居民不再通过微信群里接龙或口头登记而是登录系统在线提交申领单说明需要的物资种类、数量、理由申报数据直接进数据库不会丢、不会漏。二是审批过程留痕。管理人员打开待审核列表可以逐条通过或驳回每一次操作都记录操作人、操作时间和审批意见。谁审的、什么时候审的、为什么驳回全部有据可查。三是库存与审批联动。审核通过后系统自动扣减库存未通过则库存不动同时库存低于阈值时给出提醒。这样一来不会出现“审批通过了但库里没货”的笑话也不会出现工作人员反复核对库存的人肉开销。这个项目适合谁来参考我理解是三类人第一类是计算机相关专业学生用来做课程设计或毕业设计技术栈刚好覆盖学校要求的SpringBoot、Vue、MySQL和MyBatis第二类是刚入行一两年想写一个完整全栈Demo的初级开发想通过一个贴近真实业务的小系统把前后端协作流程跑顺第三类是准备面试的求职者把项目里权限设计、事务处理、分页查询这些点准备好面试时比背八股文更能打动面试官。2. 整体设计与架构思路2.1 角色划分与核心流程整个系统的用户角色拆成两类足够普通申报用户和管理员。普通用户能维护自己的基本信息、在线申报物资、查看自己的申报记录和审批结果管理员能管理物资信息、查看全部申报单、执行审批操作、查看库存统计。不引入超级管理员、部门管理员这种复杂分级因为对一个社区级系统来说越简单越不容易出错也越容易在答辩或面试时讲清楚设计理由。核心流程我画了一遍大致是这样用户提交申报单 - 管理员查看待审核列表 - 管理员审核通过或驳回 - 如果通过则扣减库存并生成出库记录 - 用户可以随时看到状态变更。这个流程里最关键的一点是“审核通过”和“扣减库存”必须放在同一个数据库事务里。如果先改状态再扣库存中途出错就会留下“状态已通过但库存没减”的脏数据。开发的时候如果想不到这一层后面测试阶段一定会被不稳定的数据状态折磨。2.2 技术选型为什么这样定后端选SpringBoot非常自然。它内置Tomcat、自动配置、起步依赖齐全一个较新版本的项目只需要引入Web、MyBatis、MySQL驱动、校验框架这几样就能跑起来省掉大量XML配置和繁琐的SSM整合过程。SpringBoot的生态也意味着面试时聊到的依赖注入、自动配置原理、事务管理等话题都能在这个项目上找到实际支撑。MyBatis的理由同样直接它允许你手写SQL对多表联查和复杂统计的掌控力非常强。这个项目里有几个查询天然适合写动态SQL比如条件查询物资列表、按状态筛选申报单、统计各物资的申领次数用MyBatis的if标签拼条件比JPA的规范方法更直观。半自动ORM还意味着所有人都能看懂SQL在干什么这对团队作业和答辩评审都很友好。前端选Vue是当前主流选择。Vue单页应用的组件化开发方式天然适合拆页面登录页、用户端首页、物资列表页、申报表单页、我的申报页、管理端审核页、物资管理页、库存统计页。每个页面切割成独立组件后大家并行开发也不会互相干扰。配合Element或Ant Design这类组件库表格、表单、弹窗、分页这些后台常用UI可以快速搭出来。数据库MySQL就不用多解释了开源、轻量、性能足够而且“SpringBootMyBatisMySQL”这套组合在社区里资料最多出问题随便一搜就有解决方案。2.3 数据库设计的几个关键决策表结构是这个项目的骨架我建议至少规划四张表用户表、物资表、申报记录表、库存流水表。如果你想把权限做得更规范可以把sys_user拆成sys_user sys_role sys_user_role但如果时间紧直接在用户表里加role字段也够用。用户表要包含账号、密码、姓名、联系方式、所属社区、角色标识。密码一定不能明文存储要存加密后的哈希值。项目里我用的是BCrypt加密它内部带随机盐不需要单独维护盐值字段比早期的MD5加盐方案省心得多。物资表要包含物资名称、分类、单位、库存总量、剩余库存、阈值。为什么要有“剩余库存”这个冗余字段因为申报审核是高频操作每次去count申报记录再计算剩余库存会拖慢审核接口直接在表里维护一个数字字段扣减时用事务加行锁保证不超卖简单高效。申报记录表是业务核心要包含申报人、物资、申报数量、申报理由、状态、创建时间、审核时间、审核人、审核意见。状态字段用tinyint存枚举值0待审核、1通过、2驳回后续如果需要“已领取”状态再扩展前期保持精简。库存流水表用来记录每次库存变动正数为入库、负数为出库关联操作人和操作类型。它的作用是复盘一旦有人说“我的审核通过了怎么没收到货”管理员可以从流水表里看到这笔单子到底走完了哪一步。3. 后端实现SpringBoot MyBatis 的核心落地3.1 工程结构和前置准备我习惯把后端的package按业务模块区分而不是按技术层次区分。具体到这个系统可以分成controller、service、mapper、entity、common这几个基础包再把登录接口、物资接口、申报接口分别在controller里建对应的类。这样做的目的是让代码按业务入口组织一个请求进来入口到数据层的调用链是清晰的不管是自己做维护还是交给别人接手都少走弯路。依赖方面一个可运行的pom需要包含spring-boot-starter-web、mybatis-spring-boot-starter、mysql-connector-java、lombok、spring-boot-starter-validation。另外我建议加上spring-boot-starter-aop因为后面做操作日志或统一校验时会用到不然等项目成型了再补依赖又得重新熟悉一遍切面写法。application.yml里除了数据源配置还要配MyBatis的mapper-locations、驼峰映射和日志输出。驼峰映射这个细节很重要数据库字段是remaining_stock实体类属性是remainingStock如果不开map-underscore-to-camel-case查询出来的结果会一直为null而且不报错非常容易被忽略。日志输出配成stdout后控制台能直接看到SQL语句和执行参数排查问题时效率高很多。3.2 统一返回结果与全局异常处理前后端分离项目的第一个坑往往是返回结构不一致。有的接口返回String有的返回Map前端接数据时就得疯狂判断类型维护成本极高。我一开始就定下规矩所有接口返回统一的结果对象包含code、message、data三个字段。成功时code为200业务失败时code为400或500未登录时code为401。前端axios只要解析这个结构就能统一处理所有接口的报错提示。代码上定义一个R类提供静态方法success、error、errorWithMessage。controller里全部写成return R.success(data)这种形式。这个类看起来简单但它能让前端拦截器里的逻辑变得非常干净也方便后续接入Swagger或接口文档时做统一描述。全局异常处理用RestControllerAdvice加ExceptionHandler实现。方法参数校验失败会抛MethodArgumentNotValidException数据库唯一键冲突会抛DuplicateKeyException业务自定义异常会抛BusinessException。我在异常处理器里分别为这些异常类型提供返回逻辑确保不管后端哪里出错前端拿到的永远是结构一致的错误响应。这样做的收益在联调阶段特别明显你再也不用跟前端说“这个接口报错返回的是一个Map里面有个msg字段那个接口报错返回的是字符串”全部统一成一套规则。3.3 登录鉴权与角色权限拦截登录这块是面试高频点必须自己动手写一遍。方案我选用JWT生成token全程无状态服务端不需要存session。用户登录成功后后端把用户id和角色放进JWT payload里生成一个带过期时间的token返回前端前端后续请求在请求头Authorization里带上token后端写一个拦截器从请求头取token并解析解析成功放行解析失败返回401。SpringBoot里做这个拦截器有两种常见方式一是实现HandlerInterceptor并注册到WebMvcConfigurer二是写一个OncePerRequestFilter。我选择拦截器方式因为它能同时配置拦截路径和排除路径。比如登录接口、注册接口以及静态资源要排除拦截而管理端接口不仅要校验token还要额外判断角色是否是管理员。权限判断我单独抽了一个工具类从当前请求绑定的用户信息里拿role如果Role不是管理员就抛出无权限异常。这里有个细节要注意前端只是隐藏了管理入口按钮并不意味着安全后端每个管理接口都必须自己校验角色。别指望前端路由隐藏能挡住直接调接口的人这是安全意识问题也是答辩时能说出的亮点。密码加密务必用BCrypt。有的教程还在用MD5加固定盐这放在2025年的项目里确实有点拿不出手。BCrypt是spring-security-crypto包里的工具使用方式非常简单// 注册时加密存储 String encoded BCrypt.hashpw(rawPassword, BCrypt.gensalt()); // 登录时校验 boolean matched BCrypt.checkpw(rawPassword, encodedPassword);注意校验时永远不要自己去做“取出密码-解密-对比”这种操作BCrypt.checkpw内部会解析盐并重新计算哈希直接调用就能得到结果。3.4 物资申领与库存扣减的事务控制这个模块是整个项目最需要认真写的部分。审核通过的接口业务逻辑看起来就两步更新申报状态为通过、扣减库存。但如果你分开两条SQL执行没有事务保护第二步执行时数据库突然出异常状态就停留在通过而库存没变这个数据错误会直接影响后续发物资。解决方式就是在service方法上加Transactional注解。Spring会对被注解的方法执行代理增强把方法内的所有数据库操作包在一个事务里任何一个操作抛异常就整体回滚。这里有一个很多人踩过的坑同类内部调用事务失效。如果你的AdminService里a方法调用了同类b方法而b上面加了Transactional它是不会生效的因为Spring代理只能拦截外部调用同类调用直接走内部逻辑绕过了代理对象。解决办法是把事务方法定义在另一个Service类里或者通过ApplicationContext获取代理对象再调用。扣库存的SQL还要考虑并发问题。两个管理员同时审核两笔不同申请单恰好都涉及同一物资如果不加锁就可能出现“先查出剩余库存100两个事务都判断100减20没问题最后库存变成80而不是60”的结果。最直接的方案是加“行锁”SELECT remaining_stock FROM material WHERE id #{materialId} FOR UPDATE;这句SQL会在事务内锁定该物资行其他事务必须等当前事务提交后才能继续操作。拿到锁后再比较剩余库存是否充足充足则执行更新否则抛出库存不足异常。注意FOR UPDATE必须和事务在同一连接中执行才有效这也正是因为Transactional存在才能保证SQL和新执行的操作在用同一个数据库连接。3.5 MyBatis动态SQL和分页写法查询列表是这个系统里最常见的的接口形式。物资列表要看名称模糊查、按分类筛、按库存状态筛申报记录要按状态筛、按用户筛、按物资分类筛。直接用不同的Mapper方法对应不同查询条件会让Mapper接口膨胀更好的做法是写一个动态SQL。select idsearchMaterialList resultTypecom.example.entity.Material select * from material where if testkeyword ! null and keyword ! and name like concat(%, #{keyword}, %) /if if testcategory ! null and category ! and category #{category} /if if testlowStock ! null and lowStock true and remaining_stock lt; threshold /if /where order by create_time desc /select分页时可以用PageHelper这个插件写一行PageHelper.startPage(pageNum, pageSize)后面紧跟的查询就会自动拼接limit并返回PageInfo对象里面包含了总条数、总页数等分页元数据。不过我建议自己也了解一下手写limit的写法毕竟有些面试官会故意问“PageHelper底层是怎么实现的”如果你能答出它是通过拦截器拦截了MyBatis的Executor从而修改了SQL印象分会高不少。4. 前端落地Vue 项目里那些容易被忽略的细节4.1 项目创建与前后端目录约定前端项目我推荐用Vue CLI方式创建对新人和毕设场景都最稳妥。Vue 3版本也可以但如果你对Compositon API不熟Vue 2的Options API更适合快速出成果。组件库优先选Element它能直接提供表格、表单、分页组件后台管理页面上手最快。创建完项目后我做的第一件事不是写页面而是先把目录约定好。views目录下面按用户端和管理端拆分user文件夹放申报、物资浏览、我的记录页面admin文件夹放审核、物资管理、库存统计页面。components目录放公共组件比如图片上传、状态标签、搜索表单。router目录单独维护路由表下面会专门讲路由守卫。axios封装放在utils/myAxios.js。这个文件几乎每个项目都要写核心是baseURL配置、请求拦截器自动加token、响应拦截器统一处理业务错误和401状态。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) { // 统一弹错误提示 Message.error(res.message); return Promise.reject(new Error(res.message)); } return res; }, error { if (error.response error.response.status 401) { router.push(/login); } return Promise.reject(error); } );这样封装之后所有页面里的请求方法只需要写业务参数和返回结果处理不用在每个文件里重复写token拼接和错误弹窗逻辑。尤其401集中处理可以避免你漏掉某些接口的鉴权异常导致用户“看着像登录了实际已经过期”的困惑。4.2 路由守卫与页面权限控制前端路由守卫解决的是“未登录用户手动输入地址直接访问内部页面”的问题。在Vue Router里注册全局前置守卫每次路由跳转前检查router.beforeEach((to, from, next) { const token localStorage.getItem(token); if (to.path /login) { return next(); } if (!token) { return next(/login); } if (to.meta.requiresAdmin) { const role localStorage.getItem(role); if (role ! ADMIN) { return next(/); } } next(); });这里有个容易犯的错你以为登录页写好了登录成功后会主动跳转首页所以没加登录页排除逻辑导致已经登录的用户想退出重登时会一直在登录页和首页间跳转。添加上面代码里login路由直接放行的判断就能解决。管理端页面统一挂在/admin或其子路径下给这些路由设置meta.requiresAdmin: true。于是用户端普通账号永远只能看到自己权限范围内的页面。再次强调前端守卫是体验层面的后端接口权限校验才是安全层面的两件事都要做缺一不可。4.3 申报表单的校验与体验打磨申报页是用户使用频率最高的页面体验做得好不好直接决定这个系统的“完成度”。表单里有几个字段必须加校验物资类型必选、申请数量必填且必须大于0、申请理由必填且长度在10到100字之间。Element的表单校验规则通过rules绑定到el-form-item上就能生效。比较值得说的是申请数量的上限校验。如果只做前端校验“不能大于库存”用户提交时库存刚好够但管理员审批时发现库存被别人先申领走就可能出现驳回的情况这不算错误但体验不好。我建议在用户提交时把库存数量一并传给前端页面显示每项物资的剩余库存提交时用库存作为实时可申领上限的参考值并在提交后又做一次后端校验双保险。页面级别的打磨还有一个点用户查看“我的申报记录”时不止要能看状态还要能看为什么被驳回。很多项目只给一个status字段用户看到状态是“已驳回”却不明白原因后台又联系不上管理员非常被动。所以申报表单里预留的auditOpinion字段在用户列表里要用一个专门的列展示驳回时把管理员填写的意见原样展示出来。这个细节虽然小但会让整个系统显得很完整。4.4 管理端审核列表的交互设计管理端的审核列表是操作最密集的页面。表格要展示的信息包括申报人、联系电话、物资名称、申请数量、申报时间、当前状态最后加上操作列。操作列里根据状态动态渲染按钮待审核时显示“通过”和“驳回”其他状态显示纯文本。驳回功能我建议弹出一个输入框必填理由而不是直接调接口。理由填完提交时会进入后端校验如果理由为空则抛出异常保证“驳回必有原因”。通过按钮也要二次确认用Element的Popconfirm组件包一层让操作者明确知道自己接下来要执行的是“通过并扣减库存”避免鼠标一抖误操作。如果申报记录量大了列表必须配分页和筛选条件。筛选条件包括按状态筛选、按物资分类筛选、按申报人关键词搜索。这里后端已经支持了动态SQL前端只要把筛选表单的值拼到查询参数里传给后端即可。注意每次重新查询时要重置页码为1不然会出现你搜完第3页数据后翻页查询又跑回旧条件的诡异现象。5. 从零到启动完整部署运行过程网上很多源码下载下来跑不通大多数不是因为代码有问题而是因为环境不一致。这一节我把拿到源码后从环境准备到成功启动的完整过程写一遍。5.1 环境准备后端需要JDK 8或11、Maven 3.6以上、MySQL 5.7或8.0。前端需要Node.js 14以上版本和npm或yarn。这些最好先装好并确认版本避免启动时报各种莫名其妙的版本冲突。JDK我一般配JAVA_HOME环境变量Maven配MAVEN_HOME并用阿里云镜像加速依赖下载。Node建议用长期支持版不要装测试版否则某些依赖编译会失败。5.2 数据库初始化源码里会有一个.sql文件。打开MySQL命令行先建库再把SQL文件导入mysql -uroot -p create database community_material default character set utf8mb4; use community_material; source /path/to/your/init.sql;注意SQL文件的编码格式。如果你用Windows的记事本编辑过SQL文件保存为UTF-8时可能产生文件头BOM导入时报错但报错信息不直观。用VS Code打开另存时选择“带BOM的UTF-8”往往能规避问题。导入后重点检查两张表是否有初始数据。sys_user表要有一条管理员账号material表要有几条测试物资。如果SQL文件没有初始数据你需要手动插入一条管理员账号密码用BCrypt生成。可以用在线工具生成哈希也可以在项目启动后通过一个临时接口注册再在数据库里把role改为admin。5.3 后端配置与启动打开application.yml修改数据库连接spring: datasource: url: jdbc:mysql://localhost:3306/community_material?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的密码这里两个参数容易出错一是serverTimezone。MySQL 8.0默认时区不匹配时启动会报错指定为Asia/Shanghai即可。第二个是useSSL。本地开发不需要SSL写成false能省去警告日志。之后用Maven启动后端mvn clean package -DskipTests java -jar target/community-system.jar如果控制台出现Tomcat started on port(s): 8080后端就起来了。你可以在浏览器直接在地址栏访问接口地址比如登录接口用POST请求提交账号密码能返回JSON就说明连接成功。5.4 前端启动与联调进入前端项目目录先安装依赖npm install如果下载慢执行npm config set registry https://registry.npmmirror.com再装。安装成功后npm run serve默认会起在8080端口但后端已经占用8080所以前端会改到8081或询问是否换端口。这里的处理方式是修改前端的proxy配置让所有API请求都代理到后端端口。在vue.config.js里配置module.exports { devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } };这样前端代码里请求地址写成/api/login实际会被代理转发到http://localhost:8080/api/login开发阶段完美绕开跨域问题。生产环境则需要把前端构建产物交给Nginx并配置location /api的反向代理原理一样。启动后如果发现页面能打开但登录后报“Invalid token”大概率是后端返回的token字段名跟前端读取的字段对不上。检查后端R对象返回时token是放在data字段里还是单独的data.token前端相应调整即可。类似的联调问题在第6章我整理了速查表。6. 常见问题与调试实录6.1 数据库时区错误启动后端时报The server time zone value Öйú±ê׼ʱ¼ä is unrecognized这是因为MySQL服务器时区没设定而连接串里显式配置了serverTimezone为某一个值。解决方式就是在URL里加上serverTimezoneAsia/Shanghai同时检查MySQL的系统时区不需要额外设置。6.2 跨域请求被拦截前端页面在8081端口后端接口在8080端口浏览器会拦截跨域请求。很多人第一反应是去后端写CrossOrigin或邮一个全局CORS配置类这在开发联调时确实可行但如果你用了本地代理proxy方案浏览器请求的相对路径是当前域代理帮你转发到后端就不存在跨域了。我的建议是统一走代理既省事又接近生产环境的行为模式。如果一定要用后端跨域配置注意设置allowedOriginPatterns时用*只在开发环境使用生产环境配具体域名更安全。还要允许Authorization请求头否则带token的请求会被浏览器预检拦截掉。6.3 查询结果丢失部分字段物资列表查出来剩余库存和阈值字段是null但数据库中确实有值。99%的概率是MyBatis驼峰映射没配置。检查配置mybatis: configuration: map-underscore-to-camel-case: true这个配置加上之后数据表的remaining_stock才能正确映射到实体类的remainingStock属性。如果没加就不用去纠结SQL问题先把这个开关打开再说。6.4 库存出现负数的排查申请表提交数量大导致审核通过后库存扣成负数说明事务和行锁没有生效。先检查service方法是否是被外部调用其次检查是否有多个controller路径都能触发审核操作绕过同一个service。还有一种可能是审核状态下同时存在多个线程调用没有用FOR UPDATE锁行。建议在“通过审核”接口上压测一次并发请求如果失败再回头查锁。6.5 前端打包后接口请求404开发时npm run serve一切正常npm run build后部署到Nginx刷新页面发现404或接口404。前者的常见原因是Nginx没有配置try_files兜底location / { try_files $uri $uri/ /index.html; }因为Vue是单页应用路由跳转靠history API比如直接访问/admin/material时服务器找不到这个文件必须回退到index.html。接口404则是因为Nginx没有配置/api代理把接口请求转发到后端服务。配置方式前面已经提过。下面整理一份速查表项目里最高频出现的五类问题直接对照排查现象常见原因排查方式登录接口报500数据库连接失败或时区错看控制台异常检查application.yml数据源接口返回401token缺失或过期查看浏览器Network里请求头是否有Authorization列表数据为null驼峰映射未开启检查mybatis配置中的map-underscore-to-camel-case页面空白但无报错前端路由history模式刷新检查Nginx配置try_files回退审核后库存没变事务未生效或SQL顺序错误确认审核方法在独立service类且扣库存SQL在锁内7. 做完项目之后还能怎么往深处走7.1 从“能跑”到“能讲清楚”通过这个项目能学到的东西绝对不只是复制粘贴源码跑通。最值得花时间的地方是把每一个模块的“为什么要这么设计”想明白。比如为什么库存要用单独字段而不是实时推算为什么审批要记录审计字段为什么前端路由守卫不能替代后端权限验证。这些设计决策其实每个都能成为答辩和面试的加分回答。另一个建议是补单元测试。很多人做这类项目都会忽略测试但如果你想在简历上写“熟悉事务边界”或“有并发场景经验”就必须有一套至少覆盖核心业务逻辑的测试用例。用SpringBootTest MockMvc测一下登录、申报、审核通过扣库存、库存不足驳回这四个核心流程代码量不多但对提升项目可信度帮助很大。7.2 可以顺带扩展的方向项目跑通并解决了基础问题之后如果你想把它做得更有厚度可以考虑四个扩展点。第一个是导入导出功能。物资清单和申报记录支持Excel导入导出管理员可以高效做数据汇总上报。后端用EasyExcel前端配合一个下载按钮代码量不大但对“管理系统”的完整度提升是肉眼可见的。第二个是消息通知。审核结果不再只是列表里的状态字段而是当申报状态变化时给用户生成一条站内通知用户登录后能看到“您的申报单XXX已被驳回理由库存不足”。这个模块需要增加通知表和消息模板工作量适中逻辑也不复杂。第三个是库存预警。在管理端首页加一个物资看板低于阈值的物资标红展示提醒管理员及时补货。有精力的话可以加一个定时任务每天9点给管理员发一封汇总邮件。第四个是把权限系统换掉引入Spring Security或Sa-Token。这两个框架帮你把登录、鉴权、方法级权限都规范化代码会多一点但项目会从“学生作品”向“工程化项目”迈进一大步。这个扩展如果能认真做下去整个项目的含金量会完全不同。我个人做完这个项目最大的体会是这类“管理系统”源码网上一搜一大把代码本身不稀奇真正有价值的是你从零把它跑通、改出问题再解决的过程。拿到源码不要急着开跑先自己把表结构画一遍把申报到审核到扣库存的链路在纸上走一遍再去看代码你会发现顺畅很多。把日志打开把一个申报单从提交到审核的SQL执行记录完整看一遍比背十遍事务传播机制都管用。项目不出问题的时候看不出差距真正把数据改出错、再靠日志和断点找回来那种手感才是这类全栈项目真正留给你的东西。