From ce6846f92033a062919daa45235cd5c6ab33d09d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=84=A6=E9=BE=99=E8=A8=80?= Date: Fri, 24 Jul 2026 16:58:26 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=87=E6=A1=A3=EF=BC=9A=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=89=98=E7=9B=98=E4=B8=9A=E5=8A=A1=E7=B3=BB=E7=BB=9F=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...026-07-24-pallet-workflow-system-design.md | 440 ++++++++++++++++++ 1 file changed, 440 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-24-pallet-workflow-system-design.md diff --git a/docs/superpowers/specs/2026-07-24-pallet-workflow-system-design.md b/docs/superpowers/specs/2026-07-24-pallet-workflow-system-design.md new file mode 100644 index 0000000..2930ce6 --- /dev/null +++ b/docs/superpowers/specs/2026-07-24-pallet-workflow-system-design.md @@ -0,0 +1,440 @@ +# 智链云枢托盘业务系统设计 + +## 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`,继续采用现有分层: + +```text +module_business/ +├── controller/ +├── service/ +├── dao/ +├── entity/ +│ ├── do/ +│ └── vo/ +└── domain/ +``` + +模块内部职责划分: + +- `master`:菌种、客户、客户现场、产线、仓库、托盘及用户授权。 +- `operation`:首次入库、工厂装填、发货、现场使用、退回。 +- `report`:六类只读报表和托盘追溯。 +- `domain`:托盘状态枚举、状态迁移规则和跨业务公共校验。 + +Controller 只负责协议、参数、认证和权限依赖;Service 负责事务、状态机、数据范围和幂等;DAO 负责持久化查询;前端不得直接指定最终库存或最终状态。 + +### 3.2 Web + +新增以下目录,沿用现有页面与请求风格: + +```text +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. 托盘状态机 + +```text +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 菜单结构 + +```text +系统管理 +├─ 基础资料维护 +│ ├─ 菌种资料 +│ ├─ 客户资料 +│ ├─ 客户现场 +│ ├─ 产线资料 +│ ├─ 仓库资料 +│ └─ 托盘资料 +├─ 业务管理 +│ ├─ 仓库入库 +│ ├─ 工厂装填 +│ ├─ 发货出库 +│ ├─ 客户现场使用 +│ └─ 退回仓库 +└─ 报表查询 + ├─ 托盘库存 + ├─ 装填记录 + ├─ 发货出库 + ├─ 产线消耗 + ├─ 退回仓库 + └─ 托盘追溯 +``` + +三个目录节点不设置权限字符串。页面菜单使用 `*: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 数据库残留和补丁应用后验收项。