SpringBoot仓库管理系统毕设:从零搭建与新手避坑指南
摘要:许多计算机专业学生在毕业设计中选择“SpringBoot仓库管理系统”作为课题,却常因缺乏工程经验陷入架构混乱、事务失效或接口设计不合理等困境。本文面向新手,基于Spring Boot 3.x,结合MyBatis-Plus与RESTful规范,提供一套可运行、易扩展的最小可行系统方案。读者将掌握模块划分、基础CRUD实现、JWT鉴权集成及前后端联调技巧,并规避常见开发陷阱,快速完成高质量毕设。
1. 背景痛点:新手最容易踩的五个坑
毕设选题“仓库管理系统”听起来业务简单,但真动手时,90%的同学会在以下环节翻车:
- 包结构随意命名,所有类堆在
com.example.demo根目录,后期一搜User出现十几份同名文件,当场懵圈。 - 直接调用
mapper.insert(),忘记在Service层加@Transactional,测试时数据回滚失败,老师一句“事务怎么保证”就答不上来。 - 列表接口一次性把十万条库存记录全查出来,内存直接爆炸,前端卡顿到怀疑人生。
- 登录逻辑写在
Controller里,明文密码比“123456”还直白,答辩时被问到“如何防注入”只能沉默。 - 本地跑的是H2,上线前忘记改MySQL驱动,打包到服务器一启动就
ClassNotFoundException,连夜回宿舍改代码。
如果你也中过招,下面这套最小可行架构(MVP)可以帮你一次性把坑填平。
2. 技术选型:为什么SpringBoot + MyBatis-Plus + JWT
| 对比维度 | SpringBoot+MyBatis-Plus+JWT | SSM+Shiro | SpringCloud+JPA |
|---|---|---|---|
| 学习成本 | 低,注解驱动,零XML | 高,大量XML配置 | 极高,组件过多 |
| 启动速度 | 秒级 | 分钟级 | 分钟级 |
| 代码量 | 少,MyBatis-Plus内置CRUD | 多,手写DAO | 中,JPA规范复杂 |
| 事务控制 | 注解即可 | AOP+XML | 分布式事务门槛高 |
| 鉴权扩展 | JWT无状态,天然支持多端 | Shiro依赖Session,集群麻烦 | OAuth2重武器 |
结论:毕设周期通常只有4~6周,SpringBoot+MyBatis-Plus+JWT是“能跑+能看懂+能扩展”的最优解。
3. 核心实现细节
3.1 领域模型速览
- 商品(product):
id、name、sku、unit - 仓库(warehouse):
id、name、location - 库存(inventory):
id、warehouse_id、product_id、quantity - 出入库记录(record):
id、type、warehouse_id、product_id、quantity、create_time
注意:把“库存”单独成表,而不是在商品里加字段,后续多仓库、批次、预警都能直接扩展。
3.2 包结构(Clean Architecture简化版)
com.example.wms ├── domain // 实体 ├── mapper // DAO ├── service // 业务 ├── controller // API ├── config // JWT、跨域、MyBatis-Plus └── common // 统一返回、异常、常量3.3 RESTful API 设计示例
| 业务 | 方法 | URL | 语义 |
|---|---|---|---|
| 商品列表 | GET | /api/products?page=1&size=20 | 分页查询 |
| 新建商品 | POST | /api/products | 新增 |
| 修改商品 | PUT | /api/products/{id} | 全量更新 |
| 删除商品 | DELETE | /api/products/{id} | 逻辑删除 |
统一返回格式:
{"code":0,"msg":"ok","data":{...}},前端无需多套判断。
3.4 基于注解的事务管理
@Service @RequiredArgsConstructor public class InventoryService { private final InventoryMapper inventoryMapper; private final RecordMapper recordMapper; @Transactional(rollbackFor = Exception.class) public void stockIn(Long warehouseId, Long productId, int amount) { // 1. 更新库存 int affected = inventoryMapper.increase(warehouseId, productId, amount); if (affected == 0) { throw new BizException("库存更新失败,可能商品不存在"); } // 2. 写入记录 Record r = Record.builder() .type(RecordType.IN) .warehouseId(warehouseId) .productId(productId) .quantity(amount) .build(); recordMapper.insert(r); } }关键注释已内嵌,新手一眼看懂“先改库存再插记录,失败一起回滚”。
4. 代码片段:让导师一眼相中
4.1 Controller:参数校验+统一返回
@RestController @RequestMapping("/api/products") @RequiredArgsConstructor @Validated public class ProductController { private final ProductService productService; @GetMapping public R<Page<ProductVO>> page(@RequestParam(defaultValue = "1") int current, @RequestParam(defaultValue = "10") int size) { Page<Product> page = productService.lambdaQuery() .page(new Page<>(current, size)); // 实体→VO脱敏 Page<ProductVO> voPage = page.convert(p -> BeanUtil.copy(p, ProductVO.class)); return R.ok(voPage); } @PostMapping public R<String> create(@Valid @RequestBody ProductDTO dto) { long id = productService.create(dto); return R.ok("创建成功,ID=" + id); } }4.2 Service:链式Lambda,拒绝SQL拼接
public LambdaQueryChainWrapper<Product> lambdaQuery() { return new LambdaQueryChainWrapper<>(mapper); }4.3 Mapper:零XML,内置方法直接复用
@Mapper public interface InventoryMapper extends BaseMapper<Inventory> { // 自定义一行SQL,防超卖 @Update("update wms_inventory set quantity = quantity + #{amount} " + "where warehouse_id = #{warehouseId} and product_id = #{productId} " + "and quantity + #{amount} >= 0") int increase(@Param("warehouseId") Long warehouseId, @Param("productId") Long productId, @Param("amount") Integer amount); }5. 安全性与性能考量
- 密码加密:使用
BCryptPasswordEncoder,强度10,已内置在Spring Security Crypto,无需额外依赖。 - 接口幂等:出库接口带
clientId+uuid作为幂等令牌,存入record表唯一索引,重复提交直接返回409 Conflict。 - SQL注入:MyBatis-Plus
#{}预编译占位,禁止${}拼接;额外开启@SqlFilter拦截器,关键字黑名单过滤。 - 分页查询:一律用
Page对象,禁止selectList后内存分页;十万级数据平均响应<200ms。 - JWT过期:accessToken 15min + refreshToken 7天,Redis续签,防止暴力撞库。
6. 生产环境避坑指南
- H2→MySQL切换:把
application-h2.yml与application-mysql.yml拆成两套,spring.profiles.active通过启动参数控制,防止打包后改配置重启。 - 静态资源路径:SpringBoot 3.x默认不再注册
WebMvcConfigurer.addResourceHandlers,需手动声明:
@Configuration public class WebConfig implements WebMvcConfigurer { public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/upload/**") .addResourceLocations("file:./upload/"); } }- 跨域:网关统一加
CorsWebFilter,允许credentials=true,否则前端带Cookie报CORS error。 - 时区:MySQL连接串追加
&serverTimezone=Asia/Shanghai,Linux服务器如为UTC,不改时间会导致库存时间差8小时。 - 日志:生产关闭
debug,logging.level.com.example.wms=info,磁盘只保留30天,防止撑爆学生机。
7. 效果展示
8. 可扩展方向(把答辩老师问倒)
- 多仓库支持:在
record表增加to_warehouse_id字段,实现库间调拨;前端仓库下拉框联动,库存列表按仓库筛选。 - 库存预警:利用
@Scheduled定时任务,扫描inventory.quantity < product.min_threshold,钉钉群机器人推送Markdown消息。 - 批次管理:增加
batch_no与expire_date,实现先进仓先出(FIFO),扫码枪对接product.sku快速出库。 - 报表大盘:基于
ECharts+MyBatis-Plus分组查询,展示月度入库曲线、热门商品Top10,导师直呼“有那味了”。
9. 结语
动手永远比看十遍教程有效。把项目拉到本地,先跑通mvn spring-boot:run,再用Postman把/api/products的增删改查点一遍,确认事务回滚、JWT鉴权、分页查询全部生效,你就拥有了能扛住答辩的“最小可用仓库系统”。接下来,不妨试着把“库存预警”模块加上,真正体会一次“需求→表设计→代码→测试”的完整闭环。祝你毕设一遍过,代码无bug,部署不宕机。