This commit is contained in:
zlwu committed 2026-05-22 11:43:41 +08:00
1 parent 5254eea4a2
commit 320f7e75f0
61 files changed
+8022 -4595

No files matched your search

@@ -0,0 +1,37 @@
-- ============================================================
-- 库位占用记录功能迁移
-- 日期: 2026-05-22
-- ============================================================
-- 1. 新增:库位占用记录表
CREATE TABLE biz_storage_slot_occupancy (
id VARCHAR(64) NOT NULL COMMENT '主键ID',
slot_id VARCHAR(64) NOT NULL COMMENT '储物格ID (FK → biz_storage_facility_slot)',
item_type VARCHAR(32) NOT NULL COMMENT '物品类型: cell_product=细胞产品, other=其他',
item_id VARCHAR(64) NOT NULL COMMENT '物品ID (指向对应业务表主键)',
item_code VARCHAR(64) NULL COMMENT '物品编码 (冗余展示)',
item_name VARCHAR(200) NULL COMMENT '物品名称 (冗余展示)',
item_source VARCHAR(32) NULL COMMENT '来源: production=生产入库, manual=手动入库, transfer=转移',
stored_at DATETIME NULL COMMENT '入库时间',
removed_at DATETIME NULL COMMENT '出库时间 (NULL=当前在库)',
occupancy_status VARCHAR(16) NOT NULL DEFAULT 'stored' COMMENT 'stored=在库, removed=已出库',
tenant_no VARCHAR(64) NULL COMMENT '租户编号',
order_num INT DEFAULT 0 COMMENT '排序',
remark VARCHAR(500) NULL COMMENT '备注',
create_by VARCHAR(64) NULL COMMENT '创建者',
create_dept_id VARCHAR(64) NULL COMMENT '创建部门',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_by VARCHAR(64) NULL COMMENT '更新者',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
del_flag CHAR(1) DEFAULT '0' COMMENT '删除标志(0存在 1删除)',
PRIMARY KEY (id),
INDEX idx_sso_slot (slot_id, occupancy_status, del_flag),
INDEX idx_sso_item (item_type, item_id, del_flag),
INDEX idx_sso_tenant (tenant_no, del_flag)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库位占用记录表';
-- 2. 增强:biz_storage_facility_slot 增加缓存字段
ALTER TABLE biz_storage_facility_slot
ADD COLUMN occupied_count INT DEFAULT 0 COMMENT '当前占用数量',
ADD COLUMN last_item_type VARCHAR(32) NULL COMMENT '最近存储物品类型',
ADD COLUMN last_stored_at DATETIME NULL COMMENT '最近入库时间';
@@ -0,0 +1,5 @@
-- 生产任务表增加任务类型字段
-- 区分入库生产单 (cell_storage) / 出库生产单 (outbound)
ALTER TABLE biz_production_task
ADD COLUMN task_type VARCHAR(20) NOT NULL DEFAULT 'cell_storage' COMMENT '任务类型 cell_storage入库生产单 outbound出库生产单'
AFTER work_order_id;
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,236 @@
# 库位占用记录功能设计文档
> 日期: 2026-05-22
> 状态: 已确认
> 范围: 前后端全做(含细胞产品对接)
## 1. 背景与需求
### 1.1 现状
库位管理模块由两张表组成:
- `biz_storage_facility`:树形结构(液氮罐 → 层 → 盒,`is_leaf=1` 为叶子存储节点)
- `biz_storage_facility_slot`:叶子节点下的储物格(如 A1-3),仅有 `slot_code` 和 `slot_status`("空"/"占用")
细胞产品表 `biz_cell_product` 通过 `storage_slot_id` FK 指向 slot,但 **slot 本身不知道存了什么**。
### 1.2 问题
1. `slot_status` 只有"空/占用",不记录物品类型、编码、来源
2. 多来源无法统一查询(想查"这格子里有什么"需逐表扫描)
3. 一个格子可存放多个物品(一对多关系),当前设计无法支持
4. 无出入库历史记录
### 1.3 需求确认
| 维度 | 决策 |
|---|---|
| 储物格与物品关系 | 一对多 |
| 物品类型 | `cell_product`(细胞产品)+ `other`(其他,兜底) |
| 入库触发 | 细胞产品自动同步 + 手动入库(其他物品) |
| 出库触发 | 细胞产品状态变更自动出库 + 手动出库 |
| 历史记录 | 需要完整出入库历史 |
| 前端展示 | 点击储物格弹窗查看明细 + 手动出入库操作 |
| 实施范围 | 前后端全做 |
## 2. 方案选型
| 方案 | 描述 | 优点 | 缺点 | 结论 |
|---|---|---|---|---|
| **A(采用)** | 独立占用表 + Slot 缓存字段 | 统一查询入口、性能好、可扩展 | 双写需事务保证 | ✅ 推荐 |
| B | 纯占用表,无缓存 | 单一数据源 | 列表页性能差 | ❌ |
| C | 只加字段到 Slot 表 | 最简单 | 不支持一对多、无历史 | ❌ 不满足需求 |
## 3. 数据模型
### 3.1 新增表 `biz_storage_slot_occupancy`
```sql
CREATE TABLE biz_storage_slot_occupancy (
id VARCHAR(64) NOT NULL COMMENT '主键ID',
slot_id VARCHAR(64) NOT NULL COMMENT '储物格ID (FK → biz_storage_facility_slot)',
item_type VARCHAR(32) NOT NULL COMMENT '物品类型: cell_product=细胞产品, other=其他',
item_id VARCHAR(64) NOT NULL COMMENT '物品ID (指向对应业务表主键)',
item_code VARCHAR(64) NULL COMMENT '物品编码 (冗余展示)',
item_name VARCHAR(200) NULL COMMENT '物品名称 (冗余展示)',
item_source VARCHAR(32) NULL COMMENT '来源: production=生产入库, manual=手动入库, transfer=转移',
stored_at DATETIME NULL COMMENT '入库时间',
removed_at DATETIME NULL COMMENT '出库时间 (NULL=当前在库)',
occupancy_status VARCHAR(16) NOT NULL DEFAULT 'stored' COMMENT 'stored=在库, removed=已出库',
-- 标准公共字段
tenant_no VARCHAR(64) NULL COMMENT '租户编号',
order_num INT DEFAULT 0 COMMENT '排序',
remark VARCHAR(500) NULL COMMENT '备注',
create_by VARCHAR(64) NULL COMMENT '创建者',
create_dept_id VARCHAR(64) NULL COMMENT '创建部门',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_by VARCHAR(64) NULL COMMENT '更新者',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
del_flag CHAR(1) DEFAULT '0' COMMENT '删除标志(0存在 1删除)',
PRIMARY KEY (id),
INDEX idx_sso_slot (slot_id, occupancy_status, del_flag),
INDEX idx_sso_item (item_type, item_id, del_flag),
INDEX idx_sso_tenant (tenant_no, del_flag)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库位占用记录表';
```
### 3.2 增强 `biz_storage_facility_slot`
```sql
ALTER TABLE biz_storage_facility_slot
ADD COLUMN occupied_count INT DEFAULT 0 COMMENT '当前占用数量',
ADD COLUMN last_item_type VARCHAR(32) NULL COMMENT '最近存储物品类型',
ADD COLUMN last_stored_at DATETIME NULL COMMENT '最近入库时间';
```
### 3.3 数据关系
```
biz_storage_facility (树: 液氮罐→层→盒)
└── biz_storage_facility_slot (储物格, 新增: occupied_count, last_item_type, last_stored_at)
├── biz_storage_slot_occupancy (占用记录, 多态关联)
│ ├── item_type='cell_product' → item_id 指向 biz_cell_product.id
│ └── item_type='other' → item_id 为手动录入的标识
└── biz_cell_product.storage_slot_id (保留, 向后兼容)
```
### 3.4 一致性保证
- **入库**:同一事务内 → 写 occupancy 记录 + 更新 slot 缓存字段(`occupied_count += 1`)
- **出库**:同一事务内 → 更新 occupancy 状态 + 更新 slot 缓存字段(`occupied_count -= 1`)
- `occupied_count` 降到 0 时,`slot_status` 改回"空"
## 4. 后端 API 与业务逻辑
### 4.1 新增/修改文件
| 文件 | 作用 |
|---|---|
| `backend/app/models/models.py` | 新增 `BizStorageSlotOccupancy` 模型 |
| `backend/app/schemas/storage_occupancy.py` | 新增,Pydantic schemas |
| `backend/app/crud/storage_occupancy.py` | 新增,CRUD 层 |
| `backend/app/api/operations.py` | 在现有 router 下新增路由 |
| `backend/app/crud/cell_product.py` | 改造:入库/出库时同步写 occupancy |
| `backend/app/models/__init__.py` | 新增导出 |
### 4.2 API 接口
所有接口挂载在 `/api/web/admin/` 下:
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | `/storage-slot-occupancies` | 查询占用记录(按 slot_id / item_type / occupancy_status 筛选) |
| POST | `/storage-slot-occupancies` | 手动入库(item_type=other) |
| PUT | `/storage-slot-occupancies/{id}/remove` | 手动出库 |
| GET | `/storage-slot-occupancies/history` | 出入库历史(支持时间范围、物品类型筛选) |
**GET `/storage-slot-occupancies` 查询参数**:
- `slot_id`(必传):储物格 ID
- `occupancy_status`(可选,默认 `stored`):stored / removed
- `item_type`(可选):cell_product / other
- `page` / `page_size`:分页
**GET `/storage-slot-occupancies/history` 查询参数**:
- `slot_id`(可选):储物格 ID
- `facility_node_id`(可选):设施节点 ID(查该节点下所有格子的历史)
- `item_type`(可选)
- `start_date` / `end_date`(可选):时间范围
- `page` / `page_size`:分页
### 4.3 CRUD 核心函数
| 函数 | 说明 |
|---|---|
| `list_occupancies(db, slot_id, status, item_type, page, page_size)` | 分页查询占用记录 |
| `list_occupancy_history(db, slot_id, facility_node_id, item_type, start_date, end_date, page, page_size)` | 出入库历史 |
| `create_occupancy(db, data, current_user)` | 创建入库记录 + 更新 slot 缓存(同一事务)。`item_type=other` 时 `item_id` 自动生成 UUID |
| `remove_occupancy(db, occupancy_id, current_user)` | 出库 + 更新 slot 缓存(同一事务) |
| `batch_create_occupancies(db, items, current_user)` | 批量入库(细胞产品批量创建时调用) |
| `remove_by_item(db, item_type, item_id, current_user)` | 按物品出库(细胞产品状态变更时调用) |
| `_refresh_slot_cache(db, slot_id)` | 内部函数:刷新 slot 的 occupied_count / last_item_type / last_stored_at / slot_status |
### 4.4 细胞产品对接
| 触发点 | 改动 |
|---|---|
| `create_cell_product()` | 创建后同步调用 `create_occupancy()` |
| `create_cell_products_batch()` | 批量创建后同步调用 `batch_create_occupancies()` |
| `create_cell_products_by_quantity()` | 同上 |
| `update_cell_product()` | 状态变为 sold/used/voided 时调用 `remove_by_item('cell_product', product_id)` |
## 5. 前端交互设计
### 5.1 库位管理页面改造
**页面**:`web-admin/src/views/operations/storage-facility/index.vue`
#### 5.1.1 储物格弹窗(新增)
点击储物格 → 弹出占用明细弹窗,包含两个 Tab:
**Tab 1:在库物品**
| 列 | 说明 |
|---|---|
| 物品类型 | Tag 标签:cell_product=细胞产品, other=其他 |
| 物品编码 | item_code |
| 物品名称 | item_name |
| 来源 | production=生产入库, manual=手动入库, transfer=转移 |
| 入库时间 | stored_at |
| 操作 | [出库] 按钮 |
**Tab 2:出入库历史**
| 列 | 说明 |
|---|---|
| 物品类型 | 同上 |
| 物品编码 | item_code |
| 物品名称 | item_name |
| 来源 | item_source |
| 入库时间 | stored_at |
| 出库时间 | removed_at |
| 状态 | Tag:stored=在库, removed=已出库 |
#### 5.1.2 手动入库对话框(新增)
弹窗底部「入库」按钮触发,表单字段:
| 字段 | 控件 | 必填 |
|---|---|---|
| 物品编码 | el-input | 是 |
| 物品名称 | el-input | 是 |
| 来源 | el-select(manual/transfer) | 是 |
| 备注 | el-input textarea | 否 |
提交后调用 `POST /storage-slot-occupancies`,刷新在库物品列表。
#### 5.1.3 库位网格增强
现有网格每个格子显示 `occupied_count` 数字角标。占用格子背景色保持橙色,角标显示数量。
### 5.2 细胞产品页面
**页面**:`web-admin/src/views/production/cell-product/index.vue`
改动极小:
- 入库/出库由后端事务自动完成,前端无需额外逻辑
- 可选:储物格列增加 tooltip 显示占用数量
### 5.3 新增前端 API
**文件**:`web-admin/src/api/admin.ts`(追加)
| 函数 | 接口 |
|---|---|
| `fetchSlotOccupancies(params)` | `GET /storage-slot-occupancies` |
| `fetchSlotOccupancyHistory(params)` | `GET /storage-slot-occupancies/history` |
| `fetchCreateOccupancy(data)` | `POST /storage-slot-occupancies` |
| `fetchRemoveOccupancy(id)` | `PUT /storage-slot-occupancies/{id}/remove` |
### 5.4 SQL 迁移文件
**文件**:`docs/sql/2026-05-22-storage-slot-occupancy.sql`
包含:
1. `CREATE TABLE biz_storage_slot_occupancy`
2. `ALTER TABLE biz_storage_facility_slot` 增加缓存字段