ETest-Vue-FastAPI/工单批次分组功能实现说明.md

261 lines
8.4 KiB
Markdown
Raw Normal View History

# 工单批次分组功能实现说明
## 需求背景
样品生成工单后,需要有一个"分组"标识来识别同一批次生成的工单,方便管理和查看。
## 解决方案
添加"工单批次"字段,用于标识同一批次生成的工单:
- `batch_id`: 批次ID使用时间戳生成唯一ID
- `batch_name`: 批次名称(用户填写的"工单名称"或自动生成)
## 实现内容
### ⚠️ 重要:数据类型修复
**问题**: 时间戳(毫秒级)超出 INTEGER 范围,导致 "Out of range value" 错误
**解决**: 将 `batch_id` 字段改为 BIGINT 类型
### 1. 数据库修改
**文件**: `add_workorder_batch_field.sql``fix_batch_id_bigint.sql`
```sql
-- 重命名字段并修改类型为 BIGINT
ALTER TABLE test_work_order
CHANGE COLUMN test_order_id batch_id BIGINT NULL COMMENT '工单批次ID用于分组标识';
-- 添加批次名称字段
ALTER TABLE test_work_order
ADD COLUMN batch_name VARCHAR(100) NULL COMMENT '工单批次名称' AFTER batch_id;
```
**如果已经执行过重命名,只需修改类型**:
```sql
-- 紧急修复:只修改字段类型
ALTER TABLE test_work_order
MODIFY COLUMN batch_id BIGINT NULL COMMENT '工单批次ID用于分组标识';
```
**执行步骤**:
```bash
# 方式1完整迁移如果还没执行过
mysql -u root -p your_database < add_workorder_batch_field.sql
# 方式2紧急修复如果已经重命名但类型错误
mysql -u root -p your_database < fix_batch_id_bigint.sql
```
### 2. 后端修改
#### 2.1 DO 模型
**文件**: `test_work_order_do.py`
```python
from sqlalchemy import BigInteger # 导入 BigInteger
batch_id = Column(BigInteger, nullable=True, comment='工单批次ID用于分组标识')
batch_name = Column(String(100), nullable=True, comment='工单批次名称')
```
**注意**: 必须使用 `BigInteger` 而不是 `Integer`,因为时间戳(毫秒级)会超出 INTEGER 范围。
#### 2.2 VO 模型
**文件**: `test_work_order_vo.py`
```python
batch_id: Optional[int] = Field(default=None, description='工单批次ID')
batch_name: Optional[str] = Field(default=None, description='工单批次名称')
```
#### 2.3 样品生成工单逻辑
**文件**: `warehouse_sample_service.py`
**生成批次ID和名称**:
```python
# 生成批次ID使用时间戳
batch_id = int(datetime.now().timestamp() * 1000) # 毫秒级时间戳
batch_name = request_model.work_order_name or f'批次-{datetime.now().strftime("%Y%m%d%H%M%S")}'
```
**创建工单时关联批次**:
```python
work_order = TestWorkOrder(
name=work_order_name,
batch_id=batch_id, # 关联批次ID
batch_name=batch_name, # 批次名称
...
)
```
#### 2.4 工单查询 DAO
**文件**: `test_work_order_dao.py`
- 添加 `batch_id``batch_name` 到查询字段
- 将查询条件从 `test_order_id` 改为 `batch_id`
### 3. 前端修改
#### 3.1 工单列表页面
**文件**: `test_work_order/index.vue`
**添加"工单分组"列**:
```vue
<el-table-column label="工单分组" align="center" prop="batchName" width="150" />
```
**修改查询参数**:
```javascript
queryParams: {
batchId: null, // 从 testOrderId 改为 batchId
...
}
```
**修改验证逻辑**:
```javascript
// 验证工单是否属于同一批次
const batchIds = [...new Set(this.selectedWorkOrders.map(wo => wo.batchId))];
if (batchIds.length > 1) {
this.$modal.msgError("选中的工单必须属于同一批次");
return;
}
```
## 使用说明
### 样品生成工单
1. 进入样品管理页面
2. 选择样品,点击"生成工单"
3. **填写"工单名称"**(可选):
- 如果填写,批次名称 = 工单名称
- 如果不填,批次名称 = `批次-20260108215713`(自动生成)
4. 提交后所有工单都会有相同的批次ID和批次名称
### 工单列表查看
1. 进入工单列表页面
2. 可以看到"工单分组"列,显示批次名称
3. 同一批次的工单会显示相同的分组名称
4. 可以通过分组名称识别和筛选工单
### 工单生成订单
1. 选择工单时,系统会验证是否属于同一批次
2. 只能选择同一批次的工单生成订单
3. 如果选择了不同批次的工单,会提示错误
## 数据示例
### 数据库数据
```
id | batch_id | batch_name | name | test_eut_id
----|-----------------|-----------------|----------------|------------
721 | 1704726433000 | 测试批次A | 测试批次A-SN001 | 119
722 | 1704726433000 | 测试批次A | 测试批次A-SN002 | 120
723 | 1704726433000 | 测试批次A | 测试批次A-SN003 | 121
724 | 1704726500000 | 批次-20260108 | SN004-入射 | 122
```
### 前端显示
| 序号 | 工单分组 | 产品SN | 测试类别 | 测试项 |
|------|----------|--------|----------|--------|
| 1 | 测试批次A | SN001 | 入射 | 测试1 |
| 2 | 测试批次A | SN002 | 入射 | 测试1 |
| 3 | 测试批次A | SN003 | 入射 | 测试1 |
| 4 | 批次-20260108 | SN004 | 入射 | 测试1 |
## 优势
1. **清晰的分组标识**:通过批次名称快速识别同一批次的工单
2. **灵活的命名**:用户可以自定义批次名称,也可以使用自动生成的名称
3. **独立的批次管理**批次ID独立于订单工单可以独立存在
4. **便于筛选**:可以通过批次名称筛选和查看工单
## 与订单的关系
- **batch_id**: 工单批次ID样品生成工单时设置标识同一批次
- **order_id**: 关联订单ID从工单生成订单后设置标识已汇总到订单
**工单状态**:
1. 新建工单: `batch_id = 时间戳`, `order_id = NULL`
2. 已汇总工单: `batch_id = 时间戳`, `order_id = 订单ID`
## 相关文件
- `add_workorder_batch_field.sql` - 数据库迁移脚本 ✅
- `test_work_order_do.py` - 工单DO模型 ✅
- `test_work_order_vo.py` - 工单VO模型 ✅
- `warehouse_sample_service.py` - 样品服务 ✅
- `test_work_order_dao.py` - 工单DAO ✅
- `test_work_order/index.vue` - 工单列表前端 ✅
## 完成时间
2026-01-08
## 最终修复DAO 层字段序列化问题
### 问题描述
数据库中有工单数据batch_id 和 batch_name 都有值),但工单列表页面不显示任何数据。
### 根本原因
DAO 层的 `select()` 语句中,部分字段(如 `batch_id`、`batch_name` 等)没有使用 `.label()` 显式指定列名。虽然 SQLAlchemy 会自动推断列名,但在序列化为 JSON 时可能导致字段名不一致。
### 解决方案
**文件**: `test_work_order_dao.py`
`get_test_work_order_list` 方法中,为所有 `TestWorkOrder` 的字段添加 `.label()`:
```python
query = (
select(
TestWorkOrder.id.label('id'),
TestWorkOrder.batch_id.label('batch_id'),
TestWorkOrder.batch_name.label('batch_name'),
TestWorkOrder.test_eut_id.label('test_eut_id'),
# ... 其他字段都添加 .label()
)
# ... 其他查询逻辑
)
```
### 为什么需要 .label()
1. **一致性**: 确保所有字段都有明确的列名
2. **序列化**: `SqlalchemyUtil.serialize_result` 依赖 `Row._asdict()` 获取列名
3. **camelCase 转换**: `CamelCaseUtil.snake_to_camel` 需要正确的 snake_case 列名
### 数据流程
```
DAO (Row对象)
→ PageUtil (调用 CamelCaseUtil.transform_result)
→ SqlalchemyUtil (Row._asdict() 转字典)
→ CamelCaseUtil (snake_case → camelCase)
→ Controller (返回 JSON)
→ 前端 (显示数据)
```
## 当前状态
**已完成** - 工单批次分组功能已全部实现并修复
### 完成清单
- [x] 数据库字段类型修复BIGINT
- [x] DO 模型更新BigInteger
- [x] VO 模型添加字段
- [x] 业务逻辑生成批次ID和名称
- [x] DAO 查询返回批次字段
- [x] 前端显示工单分组列
- [x] **DAO 层字段 label 修复** - 确保所有字段正确序列化
- [x] 工单列表正常显示批次信息
### 验证步骤
1. ✅ 重启后端服务
2. ✅ 清除浏览器缓存Ctrl + Shift + R
3. ✅ 检查浏览器开发者工具 Network 标签
4. ✅ 确认 API 返回正确的 camelCase 字段batchId, batchName
5. ✅ 工单列表显示"工单分组"列
6. ✅ 批次名称正确显示
## 相关文档
- `工单列表显示修复完成.md` - 详细的修复说明
- `工单列表不显示问题诊断.md` - 诊断步骤
- `QUICKFIX_批次ID类型错误.md` - BIGINT 类型修复
- `修复订单DAO中的test_order_id引用.md` - 订单DAO修复
## 最终完成时间
2026-01-08 23:30