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 库存一致性
托盘是业务聚合根。每次状态变更必须在一个数据库事务内同时完成:
- 校验接口权限和仓库/客户现场数据范围。
- 锁定托盘当前库存行。
- 校验当前状态、位置、数量和幂等键。
- 写入或确认业务单据。
- 更新托盘当前库存快照。
- 追加不可变托盘事件。
任何一步失败都整体回滚,多托盘单据不允许部分成功。
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 |
每个权限字符串必须同时出现在:
sys_menu.perms。- Web 的
v-hasPermi或页面权限判断。 - 后端
UserInterfaceAuthDependency。 - 对应权限测试。
未授权用户绕过 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. 实施顺序
- 保护现有未提交改动并建立验证基线。
- 删除 AI 专属代码、配置、依赖和外链菜单种子。
- 实现主数据、用户授权、二维码和状态机基础。
- 实现首次入库、装填、发货、现场使用和两阶段退回。
- 实现六类只读报表和托盘追溯。
- 同步两套初始化脚本并生成不执行的 MySQL 增量补丁。
- 执行后端、Web、权限、静态和 SQL 验证。
- 交付修改清单、验证证据、AI 数据库残留和补丁应用后验收项。