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