Files
zkxw/docs/superpowers/specs/2026-06-17-storage-facility-node-code-design.md
T
2026-06-13 19:47:22 +08:00

11 KiB
Raw Blame History

库位层级编号与储物格完整编号设计文档

日期: 2026-06-17
状态: 已确认
范围: 后端 + web-admin + SQL 迁移
关联模块: 库位管理、细胞产品库、生产任务、出库放行

1. 背景

1.1 现状

库位管理由两张表组成(见 docs/superpowers/specs/2026-05-22-storage-slot-occupancy-design.md):

  • biz_storage_facility:树形结构(液氮罐 → 层 → 盒),字段含 node_name、node_type、is_leaf,无编号字段
  • biz_storage_facility_slot:叶子节点下的储物格,slot_code 仅存盒内坐标(如 1-3)

业务展示时,储物格位置拼接为 名称路径 + 本地编码:

液氮罐A / 第1层 / 盒01 / 1-3

名称路径用 / 分隔(buildTreePathLabelFromFlat / _build_facility_path_label)。

1.2 客户新需求

  • 树形库位 每一层 都有独立 编号
  • 储物格 完整编号 = 从根到叶所有层级编号 + 储物格本地编号,段间用 - 连接
  • 示例:罐 1 → 层 2 → 盒 03 → 格 1-3 → 完整编号 1-2-03-1-3

1.3 需求确认

维度 决策
节点编号格式 单段,不含 -(字母或数字,如 1、03、A)
格子本地编码 保持 行-列(如 1-3)
完整编号生成 动态计算(节点存 node_code,格子存本地 slot_code)
编号修改策略 允许修改;修改后 批量刷新关联快照
名称 vs 编号 分离:node_name 继续用于名称路径;node_code 用于编号路径

2. 方案选型

方案 描述 结论
A(采用) 节点新增 node_code;slot_code 存本地坐标;API 返回 fullSlotCode ✅ 推荐
B slot_code 直接存完整编号 ❌ 父级改编号需批量改格子;字段冗余
C 双字段 slot_code + full_slot_code 冗余存储 ❌ 同步成本高

2.1 编号修改策略选型

方案 描述 结论
A 允许改,快照不更新 ❌
B 有子节点/格子后禁止改 ❌
C(采用) 允许改,并批量更新关联快照 ✅

3. 数据模型

3.1 biz_storage_facility 新增字段

ALTER TABLE biz_storage_facility
  ADD COLUMN node_code VARCHAR(32) NOT NULL DEFAULT '' COMMENT '节点编号(单段,不含连字符)'
  AFTER node_name;

约束与校验(应用层 + 可选 DB):

规则 说明
格式 /^[A-Za-z0-9]+$/,禁止 -
兄弟唯一 同一 parent_id + tenant_no 下 node_code 不可重复
必填 新建节点必填;迁移后存量须补全
根节点 同样必须有 node_code

索引建议:

CREATE INDEX idx_sf_parent_code ON biz_storage_facility (parent_id, tenant_no, node_code, del_flag);

3.2 biz_storage_facility_slot 不变

  • slot_code 仍存盒内本地坐标({row}-{col})
  • 不新增 full_slot_code 列

3.3 完整编号计算规则

def build_node_code_path(node_id: str, facility_by_id: dict) -> str:
    """从根到当前节点的 node_code,用 '-' 连接。例:'1-2-03'"""

def build_full_slot_code(facility_node_id: str, slot_code: str, facility_by_id: dict) -> str:
    """例:build_node_code_path(...) + '-' + slot_code → '1-2-03-1-3'"""
  • facility_node_id:格子所属叶子节点 ID
  • slot_code:本地 行-列
  • 若 slot_code 为空,仅返回节点路径编号(一般不应出现在叶子格子上)

租户内完整编号唯一性: 由树结构 + 兄弟唯一 node_code + 叶子内 slot_code 唯一共同保证;叶子内 slot_code 在同一 facility_node_id 下应已有业务约束(批量生成不重复)。

3.4 字段长度

完整编号示例:1-2-03-1-3(约 10 字符)。深层树或长编号预估 ≤ 64 字符,当前 slot_code VARCHAR(64) 足够存储本地坐标;fullSlotCode 仅 API 返回,不入库。

4. 编号变更级联(策略 C)

4.1 触发时机

update_facility 当 node_code 发生变化时,在同一事务内执行级联刷新。

4.2 影响范围计算

  1. 收集被修改节点及其 所有后代 节点 ID
  2. 找出这些节点下所有 叶子节点(is_leaf=1)
  3. 查出这些叶子下全部未删除的 biz_storage_facility_slot
  4. 对每个 slot 重新计算 fullSlotCode

4.3 快照表刷新

当前持久化 storage_slot_name 的表:

表 字段 刷新方式
biz_outbound_release_cell storage_slot_name 按 cell_product_id → biz_cell_product.storage_slot_id 匹配受影响 slot,更新为新 fullSlotCode

运行时计算、无 DB 快照的模块(改编号后自动生效,无需刷表):

  • biz_cell_product 列表/详情中的 storageSlotName(_enrich_product_dicts 动态拼接)
  • 生产任务产出/原材料展示(join 细胞产品 enrich)
  • 库位占用弹窗标题
  • 储物格下拉选项(storage-slot-options.ts)

4.4 级联服务伪代码

def cascade_refresh_slot_name_snapshots(db, *, changed_node_ids: list[str], tenant_no: str):
    affected_slot_ids = collect_slot_ids_under_nodes(db, changed_node_ids, tenant_no)
    if not affected_slot_ids:
        return 0
    facility_by_id = load_all_facilities_map(db, tenant_no)
    slot_full_map = {
        sid: build_full_slot_code(slot.facility_node_id, slot.slot_code, facility_by_id)
        for sid, slot in load_slots_by_ids(db, affected_slot_ids)
    }
    # 刷新 outbound_release_cell 快照
    updated = refresh_outbound_release_cell_slot_names(db, slot_full_map, tenant_no)
    return updated

4.5 格子本地编码变更

update_slot 当 slot_code 变化时,同样触发上述快照刷新(仅该 slot ID 范围)。

5. 后端 API

5.1 设施节点

方法 路径 变更
GET /storage-facilities 返回增加 nodeCode
POST /storage-facilities 请求体增加 nodeCode(必填)
PUT /storage-facilities/{id} 可改 nodeCode;变更时级联刷新快照

CreateBody / UpdateBody 新增:

node_code: str = Field(..., alias="nodeCode", min_length=1, max_length=32)

校验错误文案:

  • 格式非法:节点编号只能包含字母和数字,不能包含连字符
  • 兄弟重复:同级节点编号已存在

5.2 储物格

方法 路径 变更
GET /storage-facility-slots 每条增加 fullSlotCode
POST /storage-facility-slots 响应含 fullSlotCode;slot_code 仍为本地坐标
PUT /storage-facility-slots/{id} 同上;改 slot_code 时刷新快照
POST /storage-facility-slots/batch 移除 codePrefix;本地编码 {row}-{col}

5.3 统一展示字段

_slot_to_dict 增加:

"fullSlotCode": build_full_slot_code(...)

_enrich_product_dicts 中 storageSlotName 由:

{node_name_path} / {slot_code}

改为:

{fullSlotCode}

可选:保留 storageSlotPath(名称路径)供 tooltip,非必须。

6. 前端(web-admin)

6.1 库位管理页

文件: web-admin/src/views/operations/storage-facility/index.vue

区域 变更
节点弹窗 新增「节点编号」表单项(必填);hint:单段字母/数字,不含 -
左侧树 展示 [nodeCode] nodeName 或 nodeCode · nodeName
格子卡片 主文案显示 fullSlotCode(替代当前列序号 cellIndex+1)
批量生成 移除「编码前缀」;预览示例完整编号
格子编辑/占用弹窗 展示完整编号;本地坐标可编辑时仍编辑 slotCode(行-列)

货架行分组: 继续使用本地 slotCode 调用 parseSlotCode,不要对 fullSlotCode 做行/列解析。

6.2 储物格下拉

文件: web-admin/src/utils/storage-slot-options.ts

  • mapSlotsToSelectOptions:name 改为 fullSlotCode
  • 可选副标题:nodePath 作 title/tooltip

6.3 关联页面(只读展示同步)

以下页面无需改交互,随 API storageSlotName / 下拉 name 变更自动显示新格式:

  • 细胞产品库 cell-product/index.vue
  • 生产任务产出/原材料相关组件
  • 出库放行物料选择

7. 历史数据迁移

文件: docs/sql/migrations/2026-06-17-storage-facility-node-code.sql

-- 1. 加字段(允许 DEFAULT '' 过渡)
ALTER TABLE biz_storage_facility
  ADD COLUMN node_code VARCHAR(32) NOT NULL DEFAULT '' COMMENT '节点编号(单段,不含连字符)'
  AFTER node_name;

-- 2. 存量补编号(需按租户人工确认后执行,示例按 order_num)
-- UPDATE biz_storage_facility SET node_code = LPAD(CAST(order_num + 1 AS CHAR), 2, '0') WHERE node_code = '' AND del_flag = 0;

-- 3. 补编号完成后,业务侧应禁止 node_code 为空的新增/编辑

-- 4. 刷新 outbound_release_cell 快照(部署后由后端脚本或一次性 SQL 执行,依赖新编号已补全)

迁移注意:

  • 存量 slot_code 无需修改
  • 补全 node_code 后,运行一次性脚本刷新 biz_outbound_release_cell.storage_slot_name
  • 若某节点无法自动补编号,库位管理页编辑补录后再统一刷新

8. 校验与测试

8.1 单元/集成测试点

# 场景 期望
1 新建节点缺 nodeCode 400
2 nodeCode 含 - 400
3 同级重复 nodeCode 400
4 批量生成 4×6 fullSlotCode 为 {祖先}-{row}-{col}
5 修改层编号 2→5 其下所有 slot 的 fullSlotCode 前缀变化
6 修改层编号后 biz_outbound_release_cell.storage_slot_name 同步更新
7 修改格子 slot_code 关联 outbound 快照更新
8 细胞产品列表 storageSlotName 显示 fullSlotCode
9 下拉选格 选项文案为 fullSlotCode

8.2 回归页面

  • /#/operations/storage-facility — 树、节点 CRUD、批量生成、格子展示
  • 细胞产品库 — 储物格列、选格下拉
  • 生产任务执行 — 产出选格
  • 出库放行 — 储物格快照列

9. 不在范围

  • 小程序端库位展示(若未来有)
  • 生产物料库 storage_location 纯文本字段(不关联库位主数据)
  • 物理标签重打流程(系统只保证数据一致,线下标签由业务自行处理)
  • 将名称路径改为 - 分隔(名称路径仍用 /)

10. 实施文件清单

层级 文件 操作
SQL docs/sql/migrations/2026-06-17-storage-facility-node-code.sql 新增
Model backend/app/models/models.py 修改
Service backend/app/services/storage_facility_code.py(或 crud 内) 新增
CRUD backend/app/crud/storage_facility.py 修改
CRUD backend/app/crud/cell_product.py 修改 _enrich 拼接
CRUD backend/app/crud/outbound_release_material.py 可选:抽取共用刷新
API backend/app/api/operations.py 修改 schema
前端 web-admin/src/views/operations/storage-facility/index.vue 修改
前端 web-admin/src/utils/storage-slot-options.ts 修改
前端 web-admin/src/api/admin.ts 类型字段补充

评审确认项: 若本设计无异议,下一步编写实施计划(docs/superpowers/plans/2026-06-17-storage-facility-node-code.md)并开始开发。