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

8.4 KiB
Raw Permalink Blame History

工单批次分组功能实现说明

需求背景

样品生成工单后,需要有一个"分组"标识来识别同一批次生成的工单,方便管理和查看。

解决方案

添加"工单批次"字段,用于标识同一批次生成的工单:

  • batch_id: 批次ID使用时间戳生成唯一ID
  • batch_name: 批次名称(用户填写的"工单名称"或自动生成)

实现内容

⚠️ 重要:数据类型修复

问题: 时间戳(毫秒级)超出 INTEGER 范围,导致 "Out of range value" 错误 解决: 将 batch_id 字段改为 BIGINT 类型

1. 数据库修改

文件: add_workorder_batch_field.sqlfix_batch_id_bigint.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;

如果已经执行过重命名,只需修改类型:

-- 紧急修复:只修改字段类型
ALTER TABLE test_work_order 
MODIFY COLUMN batch_id BIGINT NULL COMMENT '工单批次ID用于分组标识';

执行步骤:

# 方式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

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

batch_id: Optional[int] = Field(default=None, description='工单批次ID')
batch_name: Optional[str] = Field(default=None, description='工单批次名称')

2.3 样品生成工单逻辑

文件: warehouse_sample_service.py

生成批次ID和名称:

# 生成批次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")}'

创建工单时关联批次:

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_idbatch_name 到查询字段
  • 将查询条件从 test_order_id 改为 batch_id

3. 前端修改

3.1 工单列表页面

文件: test_work_order/index.vue

添加"工单分组"列:

<el-table-column label="工单分组" align="center" prop="batchName" width="150" />

修改查询参数:

queryParams: {
  batchId: null,  // 从 testOrderId 改为 batchId
  ...
}

修改验证逻辑:

// 验证工单是否属于同一批次
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_idbatch_name 等)没有使用 .label() 显式指定列名。虽然 SQLAlchemy 会自动推断列名,但在序列化为 JSON 时可能导致字段名不一致。

解决方案

文件: test_work_order_dao.py

get_test_work_order_list 方法中,为所有 TestWorkOrder 的字段添加 .label():

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)
  → 前端 (显示数据)

当前状态

已完成 - 工单批次分组功能已全部实现并修复

完成清单

  • 数据库字段类型修复BIGINT
  • DO 模型更新BigInteger
  • VO 模型添加字段
  • 业务逻辑生成批次ID和名称
  • DAO 查询返回批次字段
  • 前端显示工单分组列
  • DAO 层字段 label 修复 - 确保所有字段正确序列化
  • 工单列表正常显示批次信息

验证步骤

  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