# 库存管理设计 ## 概述 为商品管理模块增加完整的库存管理功能,记录实物商品的入库、出库(订单)、盘点及预警,支持 SKU 级别库存。 ## 数据库设计 ### 1. inventory_transactions — 库存流水 ```sql 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 — 入库单 ```sql 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 — 入库单明细 ```sql 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 — 盘点记录 ```sql 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 — 套餐商品组成(零进整出) ```sql 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 列表及各自库存 - 快捷入库按钮 ### 路由 ```js { 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'` 的商品不自持库存,库存检查/扣减穿透到子商品;入库单也只针对子商品,不针对套餐本身