2026-08-05-inventory-management-design.md 10 KB

库存管理设计

概述

为商品管理模块增加完整的库存管理功能,记录实物商品的入库、出库(订单)、盘点及预警,支持 SKU 级别库存。

数据库设计

1. inventory_transactions — 库存流水

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='库存流水表';

2. inventory_inbound_orders — 入库单

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='入库单表';

3. inventory_inbound_items — 入库单明细

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='入库单明细表';

4. inventory_counting_records — 盘点记录

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='盘点记录表';

5. product_bundle_items — 套餐商品组成(零进整出)

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 记录

6. 现有表扩展

  • 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

API 接口

方法 接口 说明
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 流水

InventoryService 核心职责

  • createInboundOrder() — 创建入库单(状态 pending)
  • confirmInbound() — 确认入库:逐条增加 products.stock / product_skus.stock,写入流水
  • recordOutbound() — 供订单服务调用,记录出库流水
  • getStockAlerts() — 查询所有库存低于预警线的商品/SKU
  • createCounting() — 创建盘点并自动计算差异
  • confirmCounting() — 确认盘点结果,差异自动调账

套餐商品(Bundle)库存逻辑

新增 Entity / Mapper / Service:

  • entity/ProductBundleItem.java — 套餐组成实体
  • mapper/ProductBundleItemMapper.java — 套餐组成 Mapper
  • InventoryService 中新增 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 字段返回各子商品的最低库存,并附带子商品明细

管理端 UI

新增页面

InventoryManage.vue — 库存管理主页,Tabs 布局:

标签页 内容
库存流水 筛选(商品名/SKU/类型/时间范围),表格展示流水
入库单 列表 + 查看/确认/取消操作
库存预警 低于预警线的商品列表,红色高亮
盘点记录 盘点列表 + 确认操作

InventoryInboundCreate.vue — 创建入库单:

  • 供应商搜索选择
  • 动态明细行(商品选择器 → 规格选择 → 数量)
  • 备注

修改现有页面

ProductManage.vue — 商品列表每行增加「库存」按钮,点击弹窗显示:

  • 当前库存、预警下限、最近 10 条流水
  • SKU 列表及各自库存
  • 快捷入库按钮

路由

{ 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 调整
  • 非实物商品(digital/assessment/coupon/virtual)不参与库存管理
  • 套餐商品 product_type='bundle' 零进整出:自身不管理库存,通过子商品扣减实现

设计决策

  1. 库存快照字段:流水记录 before_stock 和 after_stock,即使后续修改,流水也能反映当时库存状态
  2. 入库单两阶段提交:创建(pending)→ 确认(completed),确认时才实际修改库存,支持撤销
  3. 盘点差异自动调账:确认盘点时,差异自动生成一条 type=adjustment 的流水
  4. SKU 级 vs 商品级:有 SKU 的商品按 SKU 管理库存,无 SKU 的直接按商品管理
  5. 套餐零进整出:product_type='bundle' 的商品不自持库存,库存检查/扣减穿透到子商品;入库单也只针对子商品,不针对套餐本身