Files
ShiErFeng/docs/superpowers/specs/2026-07-24-pallet-workflow-system-design.md
T

19 KiB

智链云枢托盘业务系统设计

1. 背景与目标

当前项目是基于 Vue 3、Element Plus、Pinia、Vite、FastAPI 和异步 SQLAlchemy 的通用管理后台。Web 菜单由后端 /getRouters 根据 sys_menu、sys_role_menu 动态生成,按钮权限由前端 v-hasPermi 控制,真实接口权限由后端 UserInterfaceAuthDependency 校验。

本次以用户提供的流程图为最终业务依据,完成以下目标:

  • 完整移除“AI管理”专属功能。
  • 删除“施尔丰仓库”外链菜单。
  • 在“系统管理”下新增托盘基础资料、五类业务操作和六类报表。
  • 保证菜单、页面、API、按钮权限、后端权限与数据范围一致。
  • 建立可审计、可并发控制的托盘库存和追溯模型。

2. 已确认的范围

2.1 本次包含

  • Web 前端页面、动态菜单、按钮权限、API 请求和类型约定。
  • FastAPI 后端 Controller、Service、DAO、DO、VO、状态机和权限校验。
  • MySQL、PostgreSQL 初始化脚本同步。
  • 一份仅供审查、不自动执行的 MySQL 增量补丁。
  • 直接相关的测试、README、CHANGELOG 和依赖声明。
  • 适配 Android PDA 浏览器的 Web 操作页面。

2.2 本次明确排除

  • 不修改 shierfeng-fastapi-app 下任何文件,也不兼容其现有接口逻辑。
  • 不连接、不写入、不迁移本地 MySQL。
  • 不生成或执行 PostgreSQL 增量补丁。
  • 不删除现有数据库中的 AI 业务表、模型配置、聊天配置或 Agno 运行数据。
  • 不新增在途、签收、拒收、冲销、库存调整、导入、导出或摄像头扫码功能。
  • 不预置新的业务角色,不给现有普通角色自动分配新增权限。
  • 不删除用户、角色、菜单、部门、岗位、字典、参数、通知、日志、监控或系统工具等通用功能。

3. 总体架构

3.1 后端

新增独立的 module_business,继续采用现有分层:

module_business/
├── controller/
├── service/
├── dao/
├── entity/
│   ├── do/
│   └── vo/
└── domain/

模块内部职责划分:

  • master:菌种、客户、客户现场、产线、仓库、托盘及用户授权。
  • operation:首次入库、工厂装填、发货、现场使用、退回。
  • report:六类只读报表和托盘追溯。
  • domain:托盘状态枚举、状态迁移规则和跨业务公共校验。

Controller 只负责协议、参数、认证和权限依赖;Service 负责事务、状态机、数据范围和幂等;DAO 负责持久化查询;前端不得直接指定最终库存或最终状态。

3.2 Web

新增以下目录,沿用现有页面与请求风格:

src/api/master/
src/api/business/
src/api/report/
src/views/master/
src/views/business/
src/views/report/
src/components/PalletScanInput/

PDA 页面属于 Web 页面,使用响应式布局。扫码设备按 HID 键盘输入处理:输入框自动聚焦,扫码头输入二维码后回车提交;不调用摄像头,不引入前端扫码依赖。

3.3 库存一致性

托盘是业务聚合根。每次状态变更必须在一个数据库事务内同时完成:

  1. 校验接口权限和仓库/客户现场数据范围。
  2. 锁定托盘当前库存行。
  3. 校验当前状态、位置、数量和幂等键。
  4. 写入或确认业务单据。
  5. 更新托盘当前库存快照。
  6. 追加不可变托盘事件。

任何一步失败都整体回滚,多托盘单据不允许部分成功。

4. 主数据模型

4.1 biz_strain

  • 菌种是基础品种/SKU,不代表生产批次。
  • strain_code 非空且唯一。
  • 生产批次、包装规格和本次装填包数保存在装填记录中。
  • 已被业务引用后只能停用,不能删除。

4.2 biz_warehouse

  • 保存仓库编码、名称、地址和启停状态。
  • warehouse_code 非空且唯一。
  • 通过 biz_user_warehouse 与现有 sys_user 多对多关联。

4.3 客户、现场与产线

  • biz_customer:客户主数据,客户编码唯一。
  • biz_customer_site:归属于客户,现场编码在同一客户下唯一。
  • biz_production_line:仅表示客户现场使用/消耗产线,产线编码在同一现场下唯一。
  • biz_user_site:现有用户与客户现场的多对多关联。
  • 本次不建立工厂内部装填产线。

4.4 biz_pallet

  • 保存托盘编号、容量包数、资产启停状态和固定二维码标识。
  • pallet_code、qr_token 非空且唯一,创建后不可修改。
  • 托盘创建时同时建立 biz_pallet_inventory,初始流转状态为 UNSTOCKED。
  • 二维码只编码不可推测的随机 qr_token,不包含菌种、批次、数量、客户或权限信息。
  • 使用后端 qrcode 和现有 Pillow 生成可打印二维码;二维码损坏时补打原标识,不允许换码。

5. 业务与库存模型

5.1 单据与事实表

表 用途和核心约束
biz_inbound_order 首次入库单,状态仅为 DRAFT、CONFIRMED,单号唯一。
biz_inbound_item 入库单托盘明细,同一单据内托盘唯一。
biz_filling_record 单托盘即时装填;保存菌种、批次、包装规格、初始整数包数、仓库和操作人。
biz_shipment_order 发货单,状态仅为 DRAFT、CONFIRMED,单号唯一。
biz_shipment_item 多托盘整托发货明细;包数由确认时的当前余量快照生成,不允许手填拆分。
biz_usage_record 每次现场使用事实;保存产线、本次使用包数、使用后余量和幂等键。
biz_return_order 两阶段退回单,状态为 REQUESTED、CONFIRMED。
biz_return_item 退回托盘及发起时余量快照;同一退回单内托盘唯一。
biz_pallet_inventory 每托盘唯一一行当前状态、位置、余量、当前装填记录、当前绑定产线和版本号。
biz_pallet_event 只追加追溯流水;记录来源单据、前后状态、位置、数量、操作人、时间和幂等键。

5.2 数量约束

  • 库存最小单位为“包”,所有包数均为整数。
  • 装填量和使用量必须大于零。
  • 剩余量必须满足 0 <= remaining_packages <= pallet.capacity_packages。
  • 一个托盘的同一装填周期只能包含一个菌种和一个生产批次,禁止混装。
  • 当前装填记录在余量归零后关闭;下一次装填形成新的独立记录,不覆盖历史。

5.3 位置约束

biz_pallet_inventory 在任意时刻只能表达一个有效位置:

  • 位于仓库时只能有 warehouse_id。
  • 位于客户现场时只能有 site_id。
  • UNSTOCKED 时两者都为空。
  • current_line_id 只能在客户现场第一次使用后设置,并在仓库确认退回时清空。

6. 托盘状态机

UNSTOCKED
  └─ 首次入库确认 → EMPTY_IN_WAREHOUSE
       └─ 单托盘装填 → FILLED_IN_WAREHOUSE
            └─ 发货确认 → AT_SITE_UNUSED
                 ├─ 部分使用 → AT_SITE_PARTIAL
                 ├─ 全部用完 → AT_SITE_EMPTY
                 └─ 发起退回 → RETURNING
AT_SITE_PARTIAL
  ├─ 继续部分使用 → AT_SITE_PARTIAL
  ├─ 全部用完 → AT_SITE_EMPTY
  └─ 发起退回 → RETURNING
AT_SITE_EMPTY
  └─ 发起退回 → RETURNING
RETURNING
  └─ 仓库确认收货:
       ├─ remaining_packages > 0 → FILLED_IN_WAREHOUSE
       └─ remaining_packages = 0 → EMPTY_IN_WAREHOUSE

补充规则:

  • 发货确认后直接归属目标客户现场,不增加在途或签收状态。
  • 客户现场第一次使用时选择产线,本次驻场周期内不可变更。
  • 客户发起退回后冻结使用和再次发货,直到仓库确认。
  • 仓库确认退回后解除产线绑定。
  • 带料退回保留原菌种、批次和余量,可再次整托发货;再次到达现场后重新绑定产线。
  • 空托盘必须退回仓库后才能再次装填。

7. 五类业务流程

7.1 仓库入库

  • 只处理已建档空托盘的首次入库。
  • 创建草稿,选择目标仓库,连续扫描 UNSTOCKED 托盘。
  • 草稿可编辑或删除;确认后锁定全部托盘并整单转为 EMPTY_IN_WAREHOUSE。
  • 流程图中连续出现的第二个“创建入库单”按重复节点处理。

7.2 工厂装填

  • 扫描 EMPTY_IN_WAREHOUSE 托盘。
  • 校验当前用户拥有该仓库数据权限。
  • 选择菌种,填写生产批次、包装规格和正整数包数。
  • 一次操作只处理一个托盘并即时确认。
  • 成功后创建装填记录,状态转为 FILLED_IN_WAREHOUSE。

7.3 发货出库

  • 创建草稿,选择来源仓库和目标客户现场。
  • 一张发货单允许多个托盘,只允许整托发货。
  • 当前用户必须同时拥有来源仓库和目标客户现场数据权限。
  • 确认时按托盘 ID 排序加锁;全部成功后转为 AT_SITE_UNUSED。
  • 不增加在途、签收或拒收流程。

7.4 客户现场使用

  • 用户必须具备 business:usage:add 且关联当前客户现场。
  • 扫码后后端返回托盘、菌种、批次、当前余量和允许操作。
  • 第一次使用选择该现场下的产线,并锁定至本次退回完成。
  • 使用包数必须为正整数且不得超过当前余量。
  • 使用后余量大于零时转为 AT_SITE_PARTIAL,等于零时转为 AT_SITE_EMPTY。
  • 使用请求必须携带幂等键,PDA 重复提交不得重复扣减。

7.5 退回仓库

  • 客户现场用户扫描一个或多个同现场托盘,选择目标仓库并发起退回。
  • 发起后保存余量快照,托盘转为 RETURNING,禁止继续使用。
  • 仓库人员实际收货扫码确认,必须拥有目标仓库数据权限。
  • 确认时重新校验状态和余量快照,整单成功或整单回滚。
  • 有余料时转为 FILLED_IN_WAREHOUSE,无余料时转为 EMPTY_IN_WAREHOUSE。

8. 菜单、页面、API 与权限

8.1 菜单结构

系统管理
├─ 基础资料维护
│  ├─ 菌种资料
│  ├─ 客户资料
│  ├─ 客户现场
│  ├─ 产线资料
│  ├─ 仓库资料
│  └─ 托盘资料
├─ 业务管理
│  ├─ 仓库入库
│  ├─ 工厂装填
│  ├─ 发货出库
│  ├─ 客户现场使用
│  └─ 退回仓库
└─ 报表查询
   ├─ 托盘库存
   ├─ 装填记录
   ├─ 发货出库
   ├─ 产线消耗
   ├─ 退回仓库
   └─ 托盘追溯

三个目录节点不设置权限字符串。页面菜单使用 *:list,按钮节点使用具体操作权限。

8.2 权限映射

页面 资源前缀 动作
菌种资料 master:strain list/query/add/edit/remove
客户资料 master:customer list/query/add/edit/remove
客户现场 master:site list/query/add/edit/remove/assign
产线资料 master:line list/query/add/edit/remove
仓库资料 master:warehouse list/query/add/edit/remove/assign
托盘资料 master:pallet list/query/add/edit/remove/print
仓库入库 business:inbound list/query/add/edit/remove/confirm
工厂装填 business:filling list/query/add
发货出库 business:shipment list/query/add/edit/remove/confirm
客户现场使用 business:usage list/query/add
退回仓库 business:return list/query/request/confirm
托盘库存 report:palletinventory list
装填记录 report:filling list
发货出库 report:shipment list
产线消耗 report:consumption list
退回仓库 report:return list
托盘追溯 report:pallettrace list

每个权限字符串必须同时出现在:

  1. sys_menu.perms。
  2. Web 的 v-hasPermi 或页面权限判断。
  3. 后端 UserInterfaceAuthDependency。
  4. 对应权限测试。

未授权用户绕过 Web 直接调用接口时返回现有规范的 403。拥有功能权限但没有目标仓库或客户现场关联时,同样拒绝访问。

8.3 API 前缀

  • 主数据:/master/strain、/master/customer、/master/site、/master/line、/master/warehouse、/master/pallet。
  • 业务:/business/inbound、/business/filling、/business/shipment、/business/usage、/business/return。
  • 报表:/report/pallet-inventory、/report/filling、/report/shipment、/report/consumption、/report/return、/report/pallet-trace。

各业务 Controller 提供自己的扫码查询接口,避免现场操作员被迫获得托盘主数据管理权限。所有写接口由后端重新查询当前状态,不信任前端提交的库存、位置或状态。

8.4 角色

  • 不新增固定业务角色。
  • 不写入新增菜单对应的普通角色种子。
  • 超级管理员沿用现有特例获得全部菜单和数据。
  • 其他用户通过现有角色管理分配菜单和按钮,并通过仓库/客户现场页面分配数据范围。
  • 角色授权必须包含“系统管理 → 分组目录 → 页面 → 按钮”的完整祖先链。

9. Web 页面行为

9.1 基础资料

  • 沿用现有搜索、分页、表格和弹窗表单风格。
  • 仓库和客户现场页面提供“分配用户”。
  • 托盘页面提供二维码生成、查看、打印和补打。
  • 被引用数据只允许停用;未引用数据才显示删除操作。

9.2 PDA Web

  • 页面使用大尺寸输入区和操作按钮,适配 Android PDA 浏览器。
  • 扫码输入框在成功、失败和页面恢复后重新聚焦。
  • 回车触发扫码查询,重复扫码给出明确提示。
  • 使用 crypto.randomUUID() 生成请求幂等键,不新增前端 UUID 依赖。
  • 前端展示后端返回的状态和允许动作,不自行推算业务状态。

9.3 报表

六个独立页面只提供查询、筛选和分页:

  • 托盘库存:当前状态、位置、菌种、批次、初始包数和剩余包数。
  • 装填记录:装填时间、仓库、托盘、菌种、批次、规格和包数。
  • 发货出库:发货单、客户现场、托盘和发货时余量。
  • 产线消耗:客户、现场、产线、托盘、菌种、批次和使用包数。
  • 退回仓库:退回申请、目标仓库、托盘、余量和确认时间。
  • 托盘追溯:按事件时间展示托盘完整状态、位置和数量变化。

筛选条件从日期、仓库、客户、现场、产线、菌种、批次、托盘编号和状态中按页面需要选取。本次不提供 Excel 导出。

10. AI 管理与外链菜单清理

10.1 删除范围

  • 删除后端 module_ai 全部源码。
  • 删除 utils/ai_util.py 和仅被 AI 使用的 utils/crypto_util.py。
  • 删除 Web src/api/ai、src/views/ai 和三个 AI 图标。
  • 删除 13 个 /ai/* 接口及 ai:model:*、ai:chat:* 权限。
  • 删除 AI 专属缓存命名空间、限流常量和 LiteLLM 日志配置。
  • 从后端 requirements 删除 Agno 和模型提供商 SDK。
  • 从前端 package.json 删除仅供 AI 使用的 Markdown、Monaco、UUID 等依赖,并删除 Monaco Vite 配置。
  • README 删除当前 AI 功能说明;CHANGELOG 保留历史新增记录并增加移除记录。

10.2 保留范围

  • 认证、JWT、接口权限、数据范围、自动路由扫描、缓存、日志、限流和通用上传。
  • utils/transport_crypto_util.py。
  • @vueuse/core、Pillow、PyJWT、cryptography 等共享依赖。
  • 通用 API Key 日志脱敏测试。
  • 导航栏独立“源码地址”和“文档地址”入口。

10.3 数据库残留策略

  • 两套初始化脚本删除 AI 菜单、角色关联、AI 字典和 AI 表 DDL,确保新环境不再创建 AI 功能。
  • MySQL 增量补丁删除 AI 菜单元数据、AI 字典元数据和“施尔丰仓库”菜单,但不执行。
  • 增量补丁不得包含 DROP TABLE ai_models、DROP TABLE ai_chat_config 或任何 Agno 表删除语句。
  • 现有 AI 表、行数、敏感配置和备份需求在最终交付中单独列出,后续获得明确确认后再清理。

11. SQL 交付策略

11.1 初始化脚本

MySQL 和 PostgreSQL 初始化脚本保持语义一致:

  • 删除 AI 和外链菜单种子。
  • 删除 AI 字典及 AI 表定义。
  • 新增业务表、唯一约束、检查约束和索引。
  • 新增三个目录、十一个业务/主数据页面、六个报表页面及按钮权限。
  • 不重新编号现有无关菜单、字典或序列。
  • 不给普通角色增加 sys_role_menu 记录。

11.2 MySQL 增量补丁

  • 文件名使用日期和业务含义,作为一次性、可审查补丁。
  • DDL 使用明确表名;菜单删除使用已审计的明确 ID,禁止通配删除。
  • DDL 与菜单 DML 分节书写,明确 MySQL DDL 隐式提交边界。
  • 包含业务表创建、菜单与权限插入、AI 元数据和菜单 ID 99 清理。
  • 不连接、不执行本地 MySQL。
  • 补丁应用后的 Redis 菜单、用户信息和 AI 字典缓存清理作为部署步骤记录,不在本次自动执行。

12. 错误、并发与审计

  • 认证失败、功能越权和数据范围越权沿用项目现有异常与 403 处理。
  • 非法数量、资源不存在、托盘状态冲突或单据状态冲突必须返回失败,不得伪成功。
  • 所有确认、使用和退回请求使用唯一幂等键。
  • 状态变更使用数据库行锁;多托盘按托盘 ID 排序加锁以降低死锁概率。
  • biz_pallet_inventory.version 用于并发版本校验。
  • biz_pallet_event 只追加、不修改、不删除;sys_oper_log 只作为补充审计,不能替代业务追溯。
  • 已确认单据不可编辑或删除,本次不实现冲销或补偿事件入口。

13. 验证与验收

13.1 后端

  • 运行 ruff 和相关 pytest。
  • 验证应用可以导入,OpenAPI 中不存在 /ai/*。
  • 覆盖状态机合法/非法迁移、整数数量边界、超用、幂等、数据范围和 403。
  • 覆盖入库、装填、发货、使用、退回的事务回滚行为。
  • 验证二维码生成接口输出有效图形。

13.2 Web

  • 运行项目可用的 lint/check 命令和生产构建。
  • 检查所有按钮权限和请求 URL 与后端一致。
  • 检查 PDA 扫码输入、自动聚焦、重复提交和错误展示。
  • 确认 shierfeng-fastapi-app 没有本次任务产生的改动。

13.3 静态和 SQL

  • 使用 rg 检查 AI 页面、接口、权限、依赖和配置残留。
  • 对比 MySQL、PostgreSQL 初始化脚本中的业务表和权限集合。
  • 检查 MySQL 增量补丁不包含 AI 业务表删除。
  • 检查菜单组件路径均对应真实 Web 页面。

13.4 本轮无法完成的运行态验收

由于用户选择只生成而不执行 MySQL 补丁,本轮不能真实验证:

  • 业务表在 MySQL 中的实际建表结果。
  • 运行态 /getRouters 是否返回新增菜单并移除旧菜单。
  • 不同真实角色和仓库/现场授权数据下的完整端到端流程。
  • 补丁执行后的 Redis 缓存失效行为。

这些项目必须在用户后续审查并应用补丁后执行,交付时不得表述为已通过。

14. 实施顺序

  1. 保护现有未提交改动并建立验证基线。
  2. 删除 AI 专属代码、配置、依赖和外链菜单种子。
  3. 实现主数据、用户授权、二维码和状态机基础。
  4. 实现首次入库、装填、发货、现场使用和两阶段退回。
  5. 实现六类只读报表和托盘追溯。
  6. 同步两套初始化脚本并生成不执行的 MySQL 增量补丁。
  7. 执行后端、Web、权限、静态和 SQL 验证。
  8. 交付修改清单、验证证据、AI 数据库残留和补丁应用后验收项。