为商品管理模块增加完整的库存管理功能,记录实物商品的入库、出库(订单)、盘点及预警,支持 SKU 级别库存。
CREATE TABLE IF NOT EXISTS inventory_transactions (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
product_id BIGINT NOT NULL COMMENT '商品ID',
sku_id BIGINT COMMENT 'SKU ID,NULL=商品级库存',
type VARCHAR(20) NOT NULL COMMENT '类型: inbound/outbound/adjustment/counting/refund',
direction TINYINT NOT NULL COMMENT '方向: 1=入库 -1=出库',
quantity INT NOT NULL COMMENT '变动数量(正数)',
before_stock INT NOT NULL COMMENT '变动前库存',
after_stock INT NOT NULL COMMENT '变动后库存',
reference_type VARCHAR(32) COMMENT '关联类型: inbound_order/order/counting/adjustment',
reference_id BIGINT COMMENT '关联ID',
operator_id BIGINT COMMENT '操作人ID,关联 users.id',
remark VARCHAR(500) COMMENT '备注',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_product (product_id),
INDEX idx_sku (sku_id),
INDEX idx_type (type),
INDEX idx_reference (reference_type, reference_id),
INDEX idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存流水表';
CREATE TABLE IF NOT EXISTS inventory_inbound_orders (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
order_no VARCHAR(64) NOT NULL UNIQUE COMMENT '入库单号',
supplier_id BIGINT COMMENT '供应商ID,关联 ecom_supplier.id',
supplier_name VARCHAR(200) COMMENT '供应商名称(冗余)',
status VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT '状态: pending/completed/cancelled',
total_items INT NOT NULL DEFAULT 0 COMMENT '商品总数',
operator_id BIGINT COMMENT '操作人ID,关联 users.id',
remark VARCHAR(500) COMMENT '备注',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_status (status),
INDEX idx_operator (operator_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='入库单表';
CREATE TABLE IF NOT EXISTS inventory_inbound_items (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
inbound_order_id BIGINT NOT NULL COMMENT '入库单ID',
product_id BIGINT NOT NULL COMMENT '商品ID',
sku_id BIGINT COMMENT 'SKU ID',
quantity INT NOT NULL COMMENT '入库数量',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_inbound_order (inbound_order_id),
INDEX idx_product (product_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='入库单明细表';
CREATE TABLE IF NOT EXISTS inventory_counting_records (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
product_id BIGINT NOT NULL COMMENT '商品ID',
sku_id BIGINT COMMENT 'SKU ID',
expected_stock INT NOT NULL COMMENT '系统库存',
actual_stock INT NOT NULL COMMENT '实际库存',
difference INT NOT NULL COMMENT '差异(actual - expected)',
status VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT '状态: pending/confirmed',
operator_id BIGINT COMMENT '操作人ID',
remark VARCHAR(500) COMMENT '备注',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_product (product_id),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='盘点记录表';
CREATE TABLE IF NOT EXISTS product_bundle_items (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
bundle_product_id BIGINT NOT NULL COMMENT '套餐商品ID,关联 products.id',
child_product_id BIGINT NOT NULL COMMENT '子商品ID,关联 products.id',
child_sku_id BIGINT COMMENT '子商品SKU ID,NULL=商品级',
quantity INT NOT NULL DEFAULT 1 COMMENT '子商品数量',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_bundle (bundle_product_id),
INDEX idx_child (child_product_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='套餐商品组成表';
套餐库存逻辑:
product_type='bundle',自身不管理库存(零进)type=outbound 记录products 表增加列:min_stock_alert INT DEFAULT 10 COMMENT '库存预警下限'product_skus 表增加列:min_stock_alert INT DEFAULT 10 COMMENT '库存预警下限'entity/
├── InventoryTransaction.java
├── InventoryInboundOrder.java
├── InventoryInboundItem.java
├── InventoryCountingRecord.java
mapper/
├── InventoryTransactionMapper.java
├── InventoryInboundOrderMapper.java
├── InventoryInboundItemMapper.java
├── InventoryCountingRecordMapper.java
service/
└── InventoryService.java
controller/admin/
└── AdminInventoryController.java
| 方法 | 接口 | 说明 |
|---|---|---|
| POST | /api/admin/inventory/inbound/create |
创建入库单(含明细,事务提交) |
| POST | /api/admin/inventory/inbound/list |
入库单列表(分页+筛选) |
| POST | /api/admin/inventory/inbound/detail |
入库单详情(含明细) |
| POST | /api/admin/inventory/inbound/confirm |
确认入库(状态→completed,触发库存增加+流水) |
| POST | /api/admin/inventory/inbound/cancel |
取消入库单 |
| POST | /api/admin/inventory/transactions/list |
库存流水(按商品/时间/类型筛选) |
| POST | /api/admin/inventory/counting/create |
创建盘点记录 |
| POST | /api/admin/inventory/counting/confirm |
确认盘点(差异自动调账+流水) |
| POST | /api/admin/inventory/counting/list |
盘点列表 |
| POST | /api/admin/inventory/product/stock |
商品库存详情(含预警状态) |
| POST | /api/admin/inventory/alert/list |
库存预警商品列表 |
| 位置 | 事件 | 操作 |
|---|---|---|
ProductOrderService.handlePaymentSuccess() |
支付成功扣减库存后 | 追加 direction=-1, type=outbound 流水 |
ProductOrderService.cancel() |
取消订单恢复库存后 | 追加 direction=1, type=refund 流水 |
ProductOrderService.approveRefund() |
退款恢复库存后 | 追加 direction=1, type=refund 流水 |
createInboundOrder() — 创建入库单(状态 pending)confirmInbound() — 确认入库:逐条增加 products.stock / product_skus.stock,写入流水recordOutbound() — 供订单服务调用,记录出库流水getStockAlerts() — 查询所有库存低于预警线的商品/SKUcreateCounting() — 创建盘点并自动计算差异confirmCounting() — 确认盘点结果,差异自动调账新增 Entity / Mapper / Service:
entity/ProductBundleItem.java — 套餐组成实体mapper/ProductBundleItemMapper.java — 套餐组成 MapperInventoryService 中新增 bundle 相关方法新增 API:
POST /api/admin/inventory/bundle/items — 配置套餐商品组成(子商品+数量)
POST /api/admin/inventory/bundle/items/list — 查询套餐组成
订单集成逻辑:
ProductOrderService.create() / createMultiItem(): 库存检查时,若商品为 product_type='bundle',遍历 product_bundle_items 检查每个子商品库存ProductOrderService.handlePaymentSuccess(): 若商品为 bundle,不扣主商品库存,改为逐条扣减子商品库存+记录流水ProductOrderService.cancel() / approveRefund(): 恢复各子商品库存+记录流水库存查询:
GET /api/admin/inventory/product/stock 返回 bundle 商品时,stock 字段返回各子商品的最低库存,并附带子商品明细InventoryManage.vue — 库存管理主页,Tabs 布局:
| 标签页 | 内容 |
|---|---|
| 库存流水 | 筛选(商品名/SKU/类型/时间范围),表格展示流水 |
| 入库单 | 列表 + 查看/确认/取消操作 |
| 库存预警 | 低于预警线的商品列表,红色高亮 |
| 盘点记录 | 盘点列表 + 确认操作 |
InventoryInboundCreate.vue — 创建入库单:
ProductManage.vue — 商品列表每行增加「库存」按钮,点击弹窗显示:
{ path: 'inventory', component: InventoryManage, meta: { perm: 'commerce:inventory' } }
{ path: 'inventory/inbound-create', component: InventoryInboundCreate, meta: { perm: 'commerce:inventory' } }
在「商品管理」菜单下新增「库存管理」子菜单项。
迁移编号: 168(当前最新 167),在 DatabaseInitializer.runMigrations() 末尾追加。
product_type='physical' 的实物商品生效min_stock_alert 默认值为 10,通过管理端 UI 可逐商品/SKU 调整product_type='bundle' 零进整出:自身不管理库存,通过子商品扣减实现before_stock 和 after_stock,即使后续修改,流水也能反映当时库存状态type=adjustment 的流水product_type='bundle' 的商品不自持库存,库存检查/扣减穿透到子商品;入库单也只针对子商品,不针对套餐本身