1. 项目概述:SpringBoot商品进销存系统实战
最近在技术社区看到不少同行在讨论中小型企业数字化改造的需求,其中商品进销存管理是个高频场景。恰好我刚用SpringBoot完成了一个轻量级进销存系统的开发,今天就把这个项目的完整实现思路和关键代码分享出来。这个系统特别适合初创团队或个体商户使用,核心功能包含商品管理、采购入库、销售出库、库存预警等模块,采用前后端分离架构,前端用Vue.js+ElementUI,后端基于SpringBoot 2.7.4开发。
提示:本文会重点讲解后端核心实现,源码已打包放在文末Gitee链接,包含完整的数据库脚本和API文档。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术架构设计解析
2.1 为什么选择SpringBoot
SpringBoot的自动配置特性让开发者能快速搭建Web应用。在这个项目中,我们主要利用了:
- 内嵌Tomcat容器:省去外部服务器配置
- Starter依赖:一键集成MyBatis-Plus、Redis等组件
- Actuator端点:方便监控系统运行状态
xml复制<!-- 典型POM依赖示例 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.2 数据库设计要点
商品进销存的核心是库存流水账,我们采用MySQL设计主要表结构:
| 表名 | 关键字段 | 说明 |
|---|---|---|
| product | id,sku,name,category,spec | 商品主表 |
| stock | product_id,warehouse_id,quantity | 库存表(分仓库) |
| purchase | order_no,supplier_id,total_amount | 采购单头 |
| purchase_item | purchase_id,product_id,price,qty | 采购单项 |
| sales | order_no,customer_id,total_amount | 销售单头 |
| sales_item | sales_id,product_id,price,qty | 销售单项 |
注意:库存采用"动碰"机制,任何出入库操作都会生成流水记录,确保可追溯性。
3. 核心功能实现细节
3.1 库存扣减的并发控制
高并发场景下库存扣减需要特别注意原子性操作。我们采用两种方案结合:
- 数据库乐观锁:
java复制@Update("update stock set quantity = quantity - #{qty}, version = version + 1
where product_id = #{productId} and version = #{version}")
int deductStock(@Param("productId") Long productId,
@Param("qty") Integer qty,
@Param("version") Integer version);
- Redis分布式锁:
java复制public boolean deductStockWithLock(Long productId, int qty) {
String lockKey = "stock_lock:" + productId;
try {
// 尝试获取锁
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
if (Boolean.TRUE.equals(locked)) {
// 执行库存扣减
return stockService.deductStock(productId, qty);
}
} finally {
redisTemplate.delete(lockKey);
}
return false;
}
3.2 进销存业务流实现
采购入库的典型处理流程:
java复制@Transactional
public PurchaseOrder createPurchase(PurchaseDTO dto) {
// 1. 创建采购单
PurchaseOrder order = new PurchaseOrder();
BeanUtils.copyProperties(dto, order);
purchaseMapper.insert(order);
// 2. 处理采购明细
List<PurchaseItem> items = dto.getItems().stream()
.map(item -> {
PurchaseItem entity = new PurchaseItem();
BeanUtils.copyProperties(item, entity);
entity.setPurchaseId(order.getId());
return entity;
}).collect(Collectors.toList());
// 3. 批量入库
purchaseItemService.saveBatch(items);
// 4. 更新库存
items.forEach(item -> {
stockService.increaseStock(
item.getProductId(),
dto.getWarehouseId(),
item.getQuantity()
);
});
return order;
}
4. 系统扩展与优化实践
4.1 库存预警模块实现
通过Spring Schedule实现每日库存检查:
java复制@Scheduled(cron = "0 0 9 * * ?") // 每天9点执行
public void checkLowStock() {
List<StockAlertVO> alerts = stockMapper.selectLowStock(10); // 阈值10%
if (!alerts.isEmpty()) {
alerts.forEach(alert -> {
String message = String.format(
"商品%s库存不足!当前库存%d,安全库存%d",
alert.getProductName(),
alert.getCurrentStock(),
alert.getSafetyStock()
);
emailService.sendAlert(alert.getManagerEmail(), message);
});
}
}
4.2 性能优化方案
- 二级缓存配置:
yaml复制mybatis-plus:
configuration:
cache-enabled: true
local-cache-scope: statement
- 接口响应优化:
- 使用DTO模式返回必要字段
- 复杂查询添加@Cacheable注解
- 分页查询统一使用MyBatis-Plus的Page对象
5. 部署与问题排查
5.1 多环境配置
通过profile区分不同环境:
properties复制# application-dev.properties
spring.datasource.url=jdbc:mysql://localhost:3306/inventory_dev
# application-prod.properties
spring.datasource.url=jdbc:mysql://prod-db:3306/inventory_prod
启动时指定环境:
bash复制java -jar inventory.jar --spring.profiles.active=prod
5.2 常见问题解决
- 事务不回滚问题:
- 检查方法是否为public
- 确认是否抛出RuntimeException
- 添加@Transactional(rollbackFor = Exception.class)
- MyBatis-Plus主键冲突:
java复制@TableId(type = IdType.AUTO) // 确保主键策略正确
private Long id;
6. 源码结构与使用说明
项目采用标准Maven结构:
code复制inventory-system
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── example
│ │ │ ├── config # 配置类
│ │ │ ├── controller # 控制层
│ │ │ ├── service # 业务层
│ │ │ ├── mapper # 数据层
│ │ │ └── entity # 实体类
│ │ └── resources
│ │ ├── mapper # XML文件
│ │ └── application.yml # 配置文件
│ └── test # 测试代码
├── sql # 数据库脚本
└── pom.xml # 依赖配置
启动步骤:
- 导入SQL脚本初始化数据库
- 修改application.yml中的数据库配置
- 启动InventoryApplication主类
- 访问http://localhost:8080/doc.html查看接口文档
我在实际开发中遇到过几个值得注意的情况:首先,库存扣减一定要做幂等处理,防止网络重试导致多次扣减;其次,商品分类建议采用无限级树形结构设计,方便后续扩展;最后,前端分页查询一定要和后端保持一致,避免PageHelper的误用导致内存溢出。
