仓储与库存

本页是一篇完整方案指南:以 inventory 插件(com.auraboot.inventory,命名空间 inv,依赖 product-catalog + org-management)为例,从用户场景一路讲到开发实施和典型错误。它是 hybrid 插件——18 个 model、62 条命令、30 个页面用 DSL JSON 声明,而出库扣减、自动上架、批次溯源、余额事件投递这些有状态的写逻辑由插件自带的 PF4J handler(backend/,如 ConfirmWarehouseOutHandlerAutoPutawayHandlerLotTraceHandlerInventoryBalanceEventOutbox)实现。下面每个标识符你都能在插件里 grep 到、对运行实例调得通。

另有更轻的 OSS 模板 simple-inventory(命名空间 tinv,只有产品/仓库/出入库 4 个 model),适合小型买卖存。本页讲完整 WMS。 源码(轻量模板):simple-inventory on GitHub ↗


1. 用户场景

一家电子元器件分销商,自有 raw_material(原料)、finished(成品)两类多个仓库,带库位。日常:

  • 采购到货 → 收货质检 → 上架到库位 → 库存可用;
  • 销售下单 → 系统分配/占用库存 → 生成拣货波次 → 拣货 → 出库;
  • 元器件按批次/序列号管理,过期或不良要隔离/报废并可溯源;
  • 仓间调拨需要审批;月度盘点要冻结、点数、确认差异。

仓管、销售、采购、生产、质检看到的库存视图和能做的操作各不相同。

库存查询(余额台账)—— 按商品 × 仓库的实时库存数量与金额,中文电子元器件演示数据,由入库确认自动生成

入库管理 —— 采购入库单,确认后由 handler 上账并算出总金额(状态:草稿 → 已确认)

2. 需求痛点

  • 没有实时可用量:Excel 里的「库存」既不扣占用也不并发安全,超卖频发。
  • 批次断链:哪一批料发给了哪个客户、是否在隔离期,事后查不到。
  • 操作无授权无审计:谁在什么时候改了库存、为什么,没有记录;改库存只能靠人盯。
  • 集成困难:销售单、生产领料、QC 报告想自动触发库存动作,却只能靠人工二次录入。

这些都不是「再加一张表」能解决的,而是受控状态变更的问题。

3. 常规解法 vs AuraBoot 的设计哲学

3.1 常规解法是怎样的

如果让一个团队从零做库存,通常是这条路:

  1. 领域建模:画实体和关系——产品、仓库、库位、入库单、出库单、批次、库存余额……
  2. 建表:把实体翻译成一堆 CREATE TABLE,加外键、索引、状态字段。
  3. 写 CRUD:每个实体一套 Repository / Service / Controller,增删改查全手写。
  4. 把业务规则 hard-code 进 service:出库扣减、状态流转(draft→confirmed)、「确认前必须有明细」的校验、「可用量 = 实有 − 占用」的计算——全写死在 Java 里。
  5. 各处补横切关注点:权限判断散落在每个 controller;审计靠手动插日志;想把库存变更发给下游,再搭一套 MQ。
  6. 前端:每个页面手写表单、列表、详情。
  7. 想接 AI / 自动化:对不起,得再包一层 API、再定义一遍权限和风险边界。

能跑,但业务规则散落在代码各处,改一条规则要同时动 service + controller + 前端;并发扣减、审计、事件、AI 入口每一样都得自己扛。三个月起步,而且越改越脆。

3.2 AuraBoot 的设计哲学

AuraBoot 把这件事反过来:业务的「是什么」用声明描述,「怎么执行」由运行时统一兜底。三条核心理念:

  • 元数据驱动:model / field / command / page / permission 都是声明(DSL JSON),不是手写代码。平台据此自动建表、生成接口、渲染页面——上面第 2、3、6 步基本消失。
  • 单一命令管道:所有写入都是一条命令,走同一条 命令管道。鉴权、校验、事务、审计、发事件这些横切关注点由管道统一处理,不写进每个业务里——你声明 inv:confirm_warehouse_out 时只关心「draft→confirmed + 扣库存」,并发安全和留痕是白送的(第 4、5 步从「自己扛」变成「平台默认」)。
  • 命令即契约:同一条命令同时服务 UI 按钮、自动化规则、BPM 节点、AI agent(靠 cmd_risk_level + agent_hint 分级)。AI 原生不是事后包 API,而是天生的(第 7 步免了)。

3.3 同一个功能,两种活法

于是普通做法里那些「自己扛」的事,在 AuraBoot 变成平台契约的默认能力。每一格的 AuraBoot 列都是下面能 grep 到的真实命令/模型,不是宣传话术:

能力自己写(手写后端)打包 ERP / 典型低代码AuraBoot(本插件)
出库扣减并发安全UPDATE stock SET qty=qty-?,高并发超卖,自己加锁/重试出入库表单现成,但扣减逻辑写死,改规则要二开inv:confirm_warehouse_out,命令管道内事务 + 状态机守卫,天然不超卖
可用 / 实有 / 占用三态自己加 reserved 字段,到处手维护、易漂移多数只有实有量,预留/占用要定制inv_balance 三态(inv_bal_qty / available_qty / reserved_qty),inv:allocate_inventory 统一占用
变更事件 / 可集成没有事件,靠定时批量对账闭源,想 hook 库存变更很难inv_balance_event_outbox 事件源化,下游(对账/看板/生产)订阅事件,可靠不丢
批次溯源 / 隔离自建批次表 + 手写溯源查询有批次,溯源/隔离常要加购模块inv:trace_lot / inv:quarantine_lot / inv:scrap_lot,状态机内建
被自动化 / AI 调用还要再包一层 API,且无风险分级基本无原生 AI 入口同一条命令带 cmd_risk_level + agent_hint,UI / 自动化 / BPM / AI agent 同一条路径,安全可控
权限 + 审计散落在各 controller,易漏角色粗粒度,字段级常缺五层权限 + 审计内建在命令管道,每次变更自动留痕

一句话:手写要三个月、还得自己扛并发和审计;打包 ERP 快但改不动;AuraBoot 用声明式配置就拿到一套 production 级的库存内核,而且每个能力都能被 AI 安全驱动、被你自由扩展。 下面看它具体怎么搭。

4. 功能设计

4.1 数据模型(18 个)

Model用途关键状态值
inv_warehouse / inv_warehouse_location仓库 / 库位;仓库类型 raw_material / finished / semi_finished / generalinv_warehouse_status:draft / confirmed
inv_balance按产品×仓库的实时余额字段 inv_bal_qtyinv_bal_available_qtyinv_bal_reserved_qtyinv_bal_safety_stockinv_bal_avg_cost
inv_inbound / inv_inbound_line入库单 + 明细inv_in_status:draft → confirmed
inv_outbound / inv_outbound_line出库单 + 明细
inv_transfer / inv_transfer_line调拨单 + 明细inv_transfer_status:draft → pending → approved → confirmed / cancelled
inv_pick_order / inv_pick_order_line / inv_pick_wave_common拣货单 / 行 / 波次inv_pick_status:pending / in_progress / completed / cancelled
inv_stock_check / inv_stock_check_line盘点单 + 明细
inv_lot / inv_lot_transaction批次/序列号 + 流水inv_lot_status:active / quarantine / expired / scrapped
inv_inventory_hold库存占用/冻结inv_ih_status:active / released / expired
inv_balance_event_outbox余额变更事件发件箱

4.2 命令与状态机

命令命名 inv:<动词>_<名词>。代表性命令:

命令作用
inv:confirm_warehouse_in / inv:confirm_warehouse_out确认入库 / 出库
inv:auto_putaway入库自动上架到库位
inv:allocate_inventory / inv:hold_inventory分配 / 冻结(写 inv_inventory_hold)
inv:generate_pick_order / inv:start_pick / inv:complete_pick_order生成 / 开始 / 完成拣货
inv:create_pick_wave / inv:release_wave创建 / 释放拣货波次
inv:submit_stock_check / inv:confirm_stock_check提交 / 确认盘点
inv:approve_stock_transfer / inv:confirm_stock_transfer审批 / 确认调拨
inv:quarantine_lot / inv:scrap_lot / inv:trace_lot批次隔离 / 报废 / 溯源

每条命令都是一段声明。例如 inv:confirm_warehouse_in(config/commands/inv_confirm_warehouse_in.json)的真实定义:

{
  "code": "inv:confirm_warehouse_in",
  "displayName:zh-CN": "确认入库",
  "type": "state_transition",
  "modelCode": "inv_inbound",
  "stateField": "inv_in_status",
  "fromStates": ["draft"],
  "toState": "confirmed",
  "handler": "inv:confirm_warehouse_in",
  "validation": {
    "rules": [
      { "type": "has_children", "childModel": "inv_inbound_line",
        "parentField": "inv_in_line_receipt_id", "minCount": 1,
        "message:zh-CN": "请至少添加一条入库明细后再确认" }
    ]
  },
  "permissions": ["inv.warehouse.manage"],
  "agent_hint": "Transition inv inbound status from draft to confirmed.",
  "cmd_risk_level": "L1"
}

读出来的设计信息:这是一条 state_transition(draft → confirmed),带 has_children 校验(没有明细行不让确认),要求 inv.warehouse.manage 权限,风险级 L1,并通过 agent_hint 告诉 agent 它能做什么。确认后由 handler 按行更新库存。

4.3 权限与角色

8 个权限码(<模块>.<资源>.<动作>,每资源 manage + read):

inv.warehouse.manage        inv.warehouse.read
inv.pick.manage             inv.pick.read
inv.lot.manage              inv.lot.read
inv.inventory_hold.manage   inv.inventory_hold.read

6 个角色:inv_admininv_warehouse(仓管)、inv_salesinv_purchaserinv_productioninv_quality_engineer

4.4 页面

30 个页面:各 model 的 list / detail、入出库录单、拣货作业、盘点、批次台账等;另有 2 个 dashboard。

5. 具体开发与实施

先掌握基础。本插件是 hybrid:模型 / 命令 / 页面是 DSL 声明,而出库扣减、上架、批次、余额事件投递等有状态写逻辑由插件自带的 handler(backend/)实现——二者都建立在平台核心契约上。动手前请先读:Model 与 Field · Command · 命令管道 · Permission · 插件清单 · Plugin 开发 · Page Designer

落地一个像 inventory 这样的 hybrid 插件,步骤是:

  1. 定义 model 与字段 —— config/models.json + config/fields/,字段类型用平台 dataType(string / integer / enum / decimal / date…),不要写 string(120) 这种内联长度(长度是单独字段属性)。详见 Model 与 Field
  2. 声明命令 —— 每个 model 一个 config/commands/<model>.json,状态流转用 type: state_transition + fromStates/toState,并在 permissions 绑权限码。详见 Command
    • 有状态写逻辑实现为 handler —— 像扣减、上架、批次溯源、余额事件投递这种纯声明表达不了的,在 backend/ 写 handler(实现命令对应的执行逻辑),打成 PF4J jar。简单 CRUD / 纯状态流转不需要 handler;本插件这类复杂写逻辑才用。详见 Plugin 开发
  3. 配 model-field binding —— config/bindings/,并在 resourceDirs 注册(见「典型错误」)。
  4. 设计页面 —— config/pages/,列表/表单/详情用 Page Designer 出 DSL。
  5. 权限、角色、字典、菜单 —— config/permissions.json / roles.json / dicts.json / menus.json
  6. 打包导入 —— hybrid 插件先 shadowJar 出 PF4J jar、放进平台 aura.plugins.dir 并重启加载,再用 aura CLI 的 import-directory-sync(参数是目录 path)或平台导入接口同步配置;校验返回 success:true 才算导入成功(纯 config 插件可跳过 jar 步)。注意依赖顺序:先 product-catalogorg-management,再 inventory。详见 插件清单

6. 常见配置

  • 安全库存预警:在 inv_balance.inv_bal_safety_stock 上配阈值,用自动化规则在 available_qty < safety_stock 时发命令/通知。
  • 批次策略:inv_lot_typebatch(批次)或 serial(序列号);过期/不良走 inv:quarantine_lotinv:scrap_lot,全程 inv:trace_lot 可溯源。
  • 多仓与库位:inv_warehouse_type 区分原料/成品仓;inv_warehouse_location 建库位,inv:auto_putaway 自动上架。
  • 占用与分配:销售/生产下游通过 inv:allocate_inventory / inv:hold_inventoryinv_inventory_hold,inv_ih_reference_type 标明来源(sales_order / production_order / qc_report / rma)。

7. 典型错误

  • 命令码用点号:写成 inv.warehouse.in.confirm 跑不通——真实是冒号 + 动词_名词:inv:confirm_warehouse_in
  • 绕过命令直接改库存表:直接 UPDATE inv_balance 会跳过状态机、handler 的按行更新和 inv_balance_event_outbox 事件投递,导致下游对账错乱。一切库存变更都走命令
  • 确认时没有明细行:inv:confirm_warehouse_inhas_children 校验会拒绝(「请至少添加一条入库明细」)——这是设计,不是 bug。
  • bindingRules 写进 commands.json 内联:不会被导入;必须独立 bindingRules.json 并在 resourceDirs 注册,否则报 [S-EXT-HANDLER] references unregistered handler。详见 纯配置 Plugin
  • 命令执行 payload 结构:字段放在 { "payload": { ... }, "operationType": ... },目标记录用 targetRecordId(不是 recordId)——放错位会出现「执行成功却字段为空」的迷惑性报错。

下一步

  • 系统总览 —— 插件、命令与运行时如何拼到一起
  • 命令管道 —— 上面每条命令都走的执行契约
  • 权限 —— 上面角色背后的五层模型