功能模块设计
3.1 模块总览
WMS 功能模块
├── 基础数据管理 → wms-core
├── 入库管理 → wms-inbound
├── 出库管理 → wms-outbound
├── 库存管理 → wms-inventory
├── 盘点管理 → wms-count
└── 报表看板 → wms-report
3.2 入库管理流程
3.2.1 整体状态机
ASN 创建 → 已通知 → 收货中 → 收货完成 → 上架中 → 上架完成
↓ ↓
已取消 异常
3.2.2 详细流程
Step 1: ASN 创建
│ 上游(采购系统/ERP)推送 ASN 数据
│ 包含:ASN 单号、供应商、预计到货时间、商品明细
│ 状态:已通知
│
Step 2: 收货
│ 仓库管理员扫描 ASN 单号
│ 逐 SKU 扫描条码 / 输入实收数量
│ 系统校验:实收 vs 预计
│ ├── 一致 → 收货完成
│ ├── 少于 → 标记差异,部分收货
│ └── 多于 → 标记异常(无单收货审批)
│
Step 3: 上架
│ 系统根据上架策略推荐库位
│ 管理员扫描库位码确认上架
│ 库存数量 +N,库位状态更新
│ 状态:上架完成
3.2.3 上架策略伪代码
public List<Location> recommendLocations(Sku sku, int qty) {
// 1. 查询该 SKU 已有库存的库位
List<Location> existingLocations = inventoryMapper
.findLocationsBySkuId(sku.getId());
if (!existingLocations.isEmpty()) {
return existingLocations.stream()
.filter(loc -> loc.getAvailableCapacity() >= qty)
.sorted(Comparator.comparingInt(Location::getAvailableCapacity))
.limit(3)
.collect(Collectors.toList());
}
// 2. 按 ABC 分类 + 容量筛选空库位
String abcClass = sku.getAbcClass(); // A / B / C
return locationMapper.findAvailableByAbcClass(abcClass, qty, 3);
}
3.3 出库管理流程
3.3.1 整体状态机
订单接收 → 波次生成 → 分配库存 → 拣货中 → 拣货完成 → 复核中 → 复核完成 → 已发货
↓ ↓ ↓ ↓
已取消 缺货异常 缺货标记 差异退回
3.3.2 详细流程
Step 1: 订单接收
│ OMS 推送出库订单
│ 含:订单号、客户、商品明细、期望发货时间、承运商
│
Step 2: 波次生成
│ 根据波次策略将多个订单合并为波次
│ 波次 = 拣货任务的基本单位
│
Step 3: 库存分配
│ 按 FIFO / 批次优先规则分配库存
│ 锁定库存(预占,防止被其他订单抢走)
│ 生成拣货任务清单
│
Step 4: 拣货
│ 拣货员 PDA 接收任务
│ 系统按库位排序优化拣货路径
│ 扫描库位 → 扫描商品 → 确认数量
│
Step 5: 复核
│ 扫描订单号 → 逐件扫描商品条码
│ 重量校验:实际重量 vs 理论重量
│ 差异处理
│
Step 6: 打包发货
│ 打印面单、装箱清单
│ 称重、测量体积
│ 交接承运商 → 扣减库存
3.3.3 拣货路径优化
仓库库位布局示例(数字为库位编号):
┌──────────────────────────────┐
│ 01 02 03 04 │ 21 22 23 │ ← A 区(快周转)
│ 05 06 07 08 │ 24 25 26 │
│─────────────────────────────│
│ 09 10 11 12 │ 27 28 29 │ ← B 区(中周转)
│ 13 14 15 16 │ 30 31 32 │
│─────────────────────────────│
│ 17 18 19 20 │ 33 34 35 │ ← C 区(慢周转)
└──────────────────────────────┘
↑
复核区 / 发货区
优化后路径:从靠近发货区的库位开始,
由近到远排列拣货任务
示例:需要拣 03, 18, 25 三个库位
原始顺序:03 → 18 → 25 (S型路径,效率低)
优化顺序:25 → 03 → 18 (由近及远)
3.3.4 播种式 vs 摘果式拣货
方式
描述
适用场景
摘果式
一个拣货员完成一个订单的所有商品
订单少、单品多
播种式
先汇总多个订单的总需求量,统一拣货后再按订单分播
订单多、SKU 重合度高
3.4 库存管理
3.4.1 库存维度模型
库存 = f(仓库, SKU, 库位, 批次, 状态, 货主)
查询示例:
"查询仓库1中 SKU-1001 在所有库位的可用库存"
→ SUM(quantity) WHERE warehouse_id=1 AND sku_id=1001
AND status='AVAILABLE'
3.4.2 库存状态
状态
含义
是否可分配
AVAILABLE
可用
是
LOCKED
锁定(已被订单预占)
否
FROZEN
冻结(质检中)
否
DAMAGED
残次
否
EXPIRED
过期
否
3.4.3 库存扣减时序
出库订单分配库存的时序(防止并发超扣):
┌────────────────┐
│ 开启事务 (BEGIN) │
└───────┬────────┘
▼
┌───────────────────────────────────┐
│ SELECT * FROM inventory │
│ WHERE sku_id = ? │
│ AND status = 'AVAILABLE' │
│ AND quantity > 0 │
│ ORDER BY production_date ASC │ ← FIFO: 先过期先出
│ FOR UPDATE │ ← 行锁
└───────┬───────────────────────────┘
▼
┌────────────────────────┐
│ 计算扣减量,逐行扣减 │
│ 若 quantity >= need: │
│ 扣 need,完成 │
│ 若 quantity < need: │
│ 全扣,继续下一行 │
└───────┬────────────────┘
▼
┌────────────────────┐
│ 更新库存行 │
│ 插入库存变更流水 │
└───────┬────────────┘
▼
┌──────────────────┐
│ 提交事务 (COMMIT) │
└──────────────────┘
3.4.4 安全库存预警
-- 低库存预警:当前可用库存 < 安全库存阈值
SELECT
s.sku_code,
s.sku_name,
s.safety_stock,
COALESCE(SUM(i.quantity), 0) AS current_stock
FROM sku s
LEFT JOIN inventory i
ON s.id = i.sku_id AND i.status = 'AVAILABLE'
GROUP BY s.id
HAVING current_stock < s.safety_stock;
3.5 盘点管理
3.5.1 盘点类型
类型
说明
频率
动态盘点
每日出入库结束后对变动库位复盘
每日
循环盘点
按库区轮转,每天盘一个区域
按计划
全面盘点
全仓库所有库位盘点
月度/季度
随机抽盘
随机抽取部分 SKU 或库位盘点
按需
3.5.2 盘点流程
Step 1: 创建盘点计划
│ 选择盘点类型、范围(仓库/库区/SKU)
│ 选择盘点方式:明盘(显示系统库存)/ 盲盘(不显示)
│
Step 2: 生成盘点任务
│ 按库位/库区拆分任务
│ 分配盘点人员
│ 库位冻结(可选,盘点期间禁止出入库操作)
│
Step 3: 执行盘点
│ 盘点员扫描库位码
│ 逐 SKU 扫描条码,录入实际数量
│ 系统对比账面数量
│
Step 4: 差异处理
│ 无差异 → 盘点完成
│ 有差异 → 复盘(二次盘点)
│ 复盘仍有差异 → 生成差异报告 → 主管审核
│
Step 5: 库存调整
│ 审核通过后调整库存
│ 生成盘盈/盘亏单据
│ 记录操作日志
3.5.3 盘点锁定策略
盘点锁库策略(针对部分盘点):
┌──────────────────────────────────────┐
│ 临时表:count_lock │
│ ┌──────────┬──────────┬────────────┐ │
│ │ location │ lock_type │ lock_time │ │
│ ├──────────┼──────────┼────────────┤ │
│ │ A-01-01 │ COUNTING │ 2026-07-30 │ │
│ │ A-01-02 │ COUNTING │ 2026-07-30 │ │
│ └──────────┴──────────┴────────────┘ │
│ │
│ 出入库时检查:若库位在 count_lock 中 │
│ 且 lock_type=COUNTING,则拒绝操作 │
└──────────────────────────────────────┘
3.6 库位管理
3.6.1 库位编码规则
推荐编码格式:仓库-库区-排-列-层
示例:WH01-A-03-05-02
WH01 仓库编号
A 库区(A区快周转 / B区中周转 / C区慢周转)
03 第 3 排货架
05 第 5 列
02 第 2 层
3.6.2 库位属性
属性
说明
库位编码
唯一标识
库位类型
存储位 / 拣货位 / 暂存位 / 退货位 / 残次位
容量
最大存放数量 / 最大体积 / 最大重量
状态
空闲 / 占用 / 冻结 / 禁用
ABC 分类
对应的周转率分类
3.7 事件驱动解耦
业务模块之间通过 Spring Event / RabbitMQ 解耦:
入库完成事件
├── → 库存模块:更新库存
├── → 报表模块:更新入库统计
└── → 通知模块:发送入库通知
出库完成事件
├── → 库存模块:扣减库存
├── → 报表模块:更新出库统计
└── → OMS:回传发货状态
盘点调整事件
├── → 库存模块:调整库存
└── → 审计模块:记录调整日志
// 事件定义
public class InboundCompletedEvent extends ApplicationEvent {
private final Long asnId;
private final List<InboundDetail> details;
// ...
}
// 事件发布
@Transactional
public void completeInbound(Long asnId) {
// 业务处理...
eventPublisher.publishEvent(new InboundCompletedEvent(this, asnId, details));
}
// 事件监听
@Component
public class InventoryEventListener {
@EventListener
@Transactional
public void handleInboundCompleted(InboundCompletedEvent event) {
// 更新库存
}
}