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

261 lines
8.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 工单批次分组功能实现说明
## 需求背景
样品生成工单后,需要有一个"分组"标识来识别同一批次生成的工单,方便管理和查看。
## 解决方案
添加"工单批次"字段,用于标识同一批次生成的工单:
- `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