样品管理中提交工单
parent
5a09430e31
commit
4b21dcd1ce
|
|
@ -0,0 +1,373 @@
|
|||
# Design Document
|
||||
|
||||
## Overview
|
||||
|
||||
本设计文档描述了从样品库直接生成测试工单的功能实现。该功能将简化原有的"订单→产品→工单"流程,改为"样品→工单"的直接流程。
|
||||
|
||||
核心变化:
|
||||
- 前端:在样品管理页面添加"生成工单"功能
|
||||
- 后端:创建新的API接口,直接从样品信息生成工单
|
||||
- 数据关联:工单的test_eut_id字段将直接关联warehouse_sample表的sample_id
|
||||
|
||||
## Architecture
|
||||
|
||||
### 系统架构
|
||||
|
||||
```
|
||||
前端层 (Vue.js)
|
||||
└── 样品管理页面 (warehouse/sample/index.vue)
|
||||
├── 样品列表展示
|
||||
├── 样品选择(多选)
|
||||
└── 生成工单对话框
|
||||
├── 测试流程选择
|
||||
├── 工单名称输入
|
||||
└── 备注输入
|
||||
|
||||
API层 (FastAPI)
|
||||
└── 样品工单控制器 (warehouse_sample_controller.py)
|
||||
└── POST /warehouse/sample/generate_work_orders
|
||||
├── 接收样品ID列表
|
||||
├── 接收测试流程ID
|
||||
└── 返回生成结果
|
||||
|
||||
服务层 (Service)
|
||||
└── 样品工单服务 (warehouse_sample_service.py)
|
||||
└── generate_work_orders_from_samples()
|
||||
├── 验证样品状态
|
||||
├── 获取测试流程配置
|
||||
├── 为每个样品创建工单
|
||||
└── 更新样品状态
|
||||
|
||||
数据层 (DAO)
|
||||
└── 工单数据访问 (test_work_order_dao.py)
|
||||
└── 批量创建工单记录
|
||||
```
|
||||
|
||||
### 数据流
|
||||
|
||||
```
|
||||
用户选择样品 → 选择测试流程 → 提交请求
|
||||
↓
|
||||
验证样品状态 → 获取流程配置 → 查询测试单元
|
||||
↓
|
||||
创建工单记录 → 更新样品状态 → 返回结果
|
||||
```
|
||||
|
||||
## Components and Interfaces
|
||||
|
||||
### 前端组件
|
||||
|
||||
#### 1. 样品管理页面增强 (warehouse/sample/index.vue)
|
||||
|
||||
**新增数据字段:**
|
||||
```javascript
|
||||
{
|
||||
// 工单生成对话框
|
||||
workOrderDialogVisible: false,
|
||||
// 工单表单
|
||||
workOrderForm: {
|
||||
sampleIds: [], // 选中的样品ID列表
|
||||
testFlowId: null, // 测试流程ID
|
||||
workOrderName: '', // 工单名称
|
||||
memo: '' // 备注
|
||||
},
|
||||
// 测试流程选项
|
||||
testFlowOptions: [],
|
||||
// 测试流程标签
|
||||
testFlowTags: []
|
||||
}
|
||||
```
|
||||
|
||||
**新增方法:**
|
||||
```javascript
|
||||
// 打开生成工单对话框
|
||||
handleGenerateWorkOrder()
|
||||
|
||||
// 加载测试流程选项
|
||||
loadTestFlowOptions()
|
||||
|
||||
// 获取测试流程标签
|
||||
fetchTestFlowTags(flowId)
|
||||
|
||||
// 提交工单生成
|
||||
submitWorkOrderGeneration()
|
||||
```
|
||||
|
||||
### 后端接口
|
||||
|
||||
#### 1. 样品工单生成API
|
||||
|
||||
**端点:** `POST /warehouse/sample/generate_work_orders`
|
||||
|
||||
**请求体:**
|
||||
```json
|
||||
{
|
||||
"sampleIds": [1, 2, 3],
|
||||
"testFlowId": 1,
|
||||
"workOrderName": "批次测试",
|
||||
"memo": "紧急测试"
|
||||
}
|
||||
```
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功",
|
||||
"data": {
|
||||
"successCount": 3,
|
||||
"failedCount": 0,
|
||||
"workOrderIds": [101, 102, 103],
|
||||
"failedSamples": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 测试流程查询API(复用现有)
|
||||
|
||||
**端点:** `GET /system/test_flow/list`
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "标准测试流程",
|
||||
"tags": [...]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Data Models
|
||||
|
||||
### 样品工单生成请求模型
|
||||
|
||||
```python
|
||||
class GenerateWorkOrderFromSampleModel(BaseModel):
|
||||
"""从样品生成工单的请求模型"""
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
sample_ids: List[int] = Field(description='样品ID列表')
|
||||
test_flow_id: int = Field(description='测试流程ID')
|
||||
work_order_name: Optional[str] = Field(default=None, description='工单名称')
|
||||
memo: Optional[str] = Field(default=None, description='备注')
|
||||
```
|
||||
|
||||
### 工单生成响应模型
|
||||
|
||||
```python
|
||||
class WorkOrderGenerationResponseModel(BaseModel):
|
||||
"""工单生成响应模型"""
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
success_count: int = Field(description='成功数量')
|
||||
failed_count: int = Field(description='失败数量')
|
||||
work_order_ids: List[int] = Field(description='生成的工单ID列表')
|
||||
failed_samples: List[dict] = Field(description='失败的样品信息')
|
||||
```
|
||||
|
||||
### 数据库表关系
|
||||
|
||||
```
|
||||
warehouse_sample (样品表)
|
||||
├── sample_id (主键)
|
||||
├── receipt_id (入库单ID)
|
||||
├── sample_sn (样品SN号)
|
||||
├── sample_model (样品型号)
|
||||
├── hardware_version (硬件版本)
|
||||
└── status (状态: 0-待测试, 1-测试中, 2-已完成, 3-已退回)
|
||||
|
||||
test_work_order (测试工单表)
|
||||
├── id (主键)
|
||||
├── test_eut_id (产品ID) → 关联 warehouse_sample.sample_id
|
||||
├── test_category_id (测试类别ID)
|
||||
├── test_item_id (测试单元ID)
|
||||
├── tester_id (测试人ID)
|
||||
├── reviewer_id (审核人ID)
|
||||
├── test_status (测试状态)
|
||||
└── test_step (测试步骤)
|
||||
|
||||
test_flow (测试流程表)
|
||||
├── id (主键)
|
||||
└── name (流程名称)
|
||||
|
||||
test_flow_tags (流程标签关联表)
|
||||
├── test_flow_id (流程ID)
|
||||
└── test_category_id (测试类别ID)
|
||||
|
||||
test_item (测试单元表)
|
||||
├── id (主键)
|
||||
├── test_category_id (测试类别ID)
|
||||
└── eut_type_id (产品类型ID)
|
||||
```
|
||||
|
||||
## Correctness Properties
|
||||
|
||||
*A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.*
|
||||
|
||||
### Property 1: 工单创建完整性
|
||||
|
||||
*For any* 有效的样品ID列表和测试流程ID,系统应为每个样品创建与测试流程中测试类别数量相等的工单数
|
||||
**Validates: Requirements 2.3**
|
||||
|
||||
### Property 2: 样品状态一致性
|
||||
|
||||
*For any* 成功生成工单的样品,其状态应从"待测试"(0)更新为"测试中"(1)
|
||||
**Validates: Requirements 4.4**
|
||||
|
||||
### Property 3: 工单数据关联正确性
|
||||
|
||||
*For any* 生成的工单,其test_eut_id字段应等于对应样品的sample_id
|
||||
**Validates: Requirements 3.2**
|
||||
|
||||
### Property 4: 测试类别映射正确性
|
||||
|
||||
*For any* 测试流程,生成的工单的test_category_id应属于该流程的test_flow_tags集合
|
||||
**Validates: Requirements 2.2**
|
||||
|
||||
### Property 5: 批量操作原子性
|
||||
|
||||
*For any* 样品列表中的单个样品工单创建失败,不应影响其他样品的工单创建
|
||||
**Validates: Requirements 2.4**
|
||||
|
||||
### Property 6: 工单命名规则一致性
|
||||
|
||||
*For any* 未指定工单名称的请求,生成的工单名称应遵循"样品SN + 测试类别"的格式
|
||||
**Validates: Requirements 5.5**
|
||||
|
||||
## Error Handling
|
||||
|
||||
### 错误类型
|
||||
|
||||
1. **样品不存在错误**
|
||||
- 错误码:404
|
||||
- 消息:样品ID不存在
|
||||
- 处理:跳过该样品,继续处理其他样品
|
||||
|
||||
2. **样品状态错误**
|
||||
- 错误码:400
|
||||
- 消息:样品状态不允许生成工单
|
||||
- 处理:记录失败原因,继续处理其他样品
|
||||
|
||||
3. **测试流程不存在错误**
|
||||
- 错误码:404
|
||||
- 消息:测试流程不存在
|
||||
- 处理:终止整个操作,返回错误
|
||||
|
||||
4. **测试单元未配置错误**
|
||||
- 错误码:400
|
||||
- 消息:该样品型号未配置测试单元
|
||||
- 处理:跳过该样品,记录警告
|
||||
|
||||
5. **数据库操作错误**
|
||||
- 错误码:500
|
||||
- 消息:工单创建失败
|
||||
- 处理:回滚事务,返回错误详情
|
||||
|
||||
### 错误响应格式
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 400,
|
||||
"msg": "部分样品工单生成失败",
|
||||
"data": {
|
||||
"successCount": 2,
|
||||
"failedCount": 1,
|
||||
"workOrderIds": [101, 102],
|
||||
"failedSamples": [
|
||||
{
|
||||
"sampleId": 3,
|
||||
"sampleSn": "SN003",
|
||||
"reason": "样品状态不允许生成工单"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
|
||||
1. **样品状态验证测试**
|
||||
- 测试待测试状态样品可以生成工单
|
||||
- 测试其他状态样品被正确拒绝
|
||||
|
||||
2. **测试流程配置查询测试**
|
||||
- 测试正确获取流程的测试类别
|
||||
- 测试流程不存在时的错误处理
|
||||
|
||||
3. **工单数据构造测试**
|
||||
- 测试工单字段正确映射样品信息
|
||||
- 测试工单命名规则
|
||||
|
||||
### Property-Based Tests
|
||||
|
||||
使用Python的`hypothesis`库进行属性测试:
|
||||
|
||||
1. **Property 1测试:工单创建完整性**
|
||||
```python
|
||||
@given(
|
||||
sample_ids=st.lists(st.integers(min_value=1), min_size=1, max_size=10),
|
||||
test_flow_id=st.integers(min_value=1)
|
||||
)
|
||||
async def test_work_order_creation_completeness(sample_ids, test_flow_id):
|
||||
"""测试为每个样品创建正确数量的工单"""
|
||||
# 生成工单
|
||||
result = await generate_work_orders_from_samples(sample_ids, test_flow_id)
|
||||
|
||||
# 获取流程的测试类别数量
|
||||
category_count = await get_test_category_count(test_flow_id)
|
||||
|
||||
# 验证:成功的样品数 * 测试类别数 = 工单数
|
||||
assert len(result.work_order_ids) == result.success_count * category_count
|
||||
```
|
||||
|
||||
2. **Property 2测试:样品状态一致性**
|
||||
```python
|
||||
@given(sample_ids=st.lists(st.integers(min_value=1), min_size=1))
|
||||
async def test_sample_status_consistency(sample_ids):
|
||||
"""测试成功生成工单后样品状态更新"""
|
||||
# 生成工单
|
||||
result = await generate_work_orders_from_samples(sample_ids, test_flow_id=1)
|
||||
|
||||
# 验证:所有成功的样品状态都是"测试中"
|
||||
for sample_id in sample_ids[:result.success_count]:
|
||||
sample = await get_sample_by_id(sample_id)
|
||||
assert sample.status == '1'
|
||||
```
|
||||
|
||||
3. **Property 3测试:工单数据关联正确性**
|
||||
```python
|
||||
@given(sample_id=st.integers(min_value=1))
|
||||
async def test_work_order_sample_association(sample_id):
|
||||
"""测试工单正确关联样品"""
|
||||
# 生成工单
|
||||
result = await generate_work_orders_from_samples([sample_id], test_flow_id=1)
|
||||
|
||||
# 验证:所有工单的test_eut_id都等于sample_id
|
||||
for work_order_id in result.work_order_ids:
|
||||
work_order = await get_work_order_by_id(work_order_id)
|
||||
assert work_order.test_eut_id == sample_id
|
||||
```
|
||||
|
||||
### Integration Tests
|
||||
|
||||
1. **端到端工单生成测试**
|
||||
- 创建测试样品
|
||||
- 调用工单生成API
|
||||
- 验证工单创建成功
|
||||
- 验证样品状态更新
|
||||
|
||||
2. **批量操作测试**
|
||||
- 创建多个测试样品
|
||||
- 批量生成工单
|
||||
- 验证部分失败场景
|
||||
|
||||
3. **前端集成测试**
|
||||
- 测试对话框打开和关闭
|
||||
- 测试表单验证
|
||||
- 测试API调用和响应处理
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
# Requirements Document
|
||||
|
||||
## Introduction
|
||||
|
||||
本需求文档描述了从样品库直接生成测试工单的功能。原有系统通过订单管理来生成工单,现在需求变更为直接从样品库(warehouse_sample)生成工单,简化流程。
|
||||
|
||||
## Glossary
|
||||
|
||||
- **Sample (样品)**: 入库的测试样品,包含型号、SN号、硬件版本等信息
|
||||
- **Work Order (工单)**: 测试工单,用于跟踪样品的测试流程
|
||||
- **Test Category (测试类别)**: 测试的分类,如功能测试、性能测试等
|
||||
- **Test Item (测试单元)**: 具体的测试项目
|
||||
- **Test Flow (测试流程)**: 包含多个测试类别的完整测试流程
|
||||
- **Receipt (入库单)**: 样品的入库记录
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement 1
|
||||
|
||||
**User Story:** 作为测试人员,我希望能够从样品管理页面直接选择样品生成工单,以便简化工单创建流程。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN 用户在样品管理页面选择一个或多个样品 THEN 系统应显示"生成工单"按钮
|
||||
2. WHEN 用户点击"生成工单"按钮 THEN 系统应打开工单生成对话框
|
||||
3. WHEN 用户在对话框中选择测试流程 THEN 系统应显示该流程包含的测试类别标签
|
||||
4. WHEN 用户提交工单生成请求 THEN 系统应为每个选中的样品创建对应的测试工单
|
||||
5. WHEN 工单生成成功 THEN 系统应显示成功消息并刷新样品列表
|
||||
|
||||
### Requirement 2
|
||||
|
||||
**User Story:** 作为测试人员,我希望能够为批量样品选择测试流程,以便一次性创建多个工单。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN 用户选择多个样品生成工单 THEN 系统应允许为所有样品选择相同的测试流程
|
||||
2. WHEN 用户选择测试流程 THEN 系统应从测试流程配置中获取包含的测试类别
|
||||
3. WHEN 系统创建工单 THEN 每个样品应根据测试流程中的测试类别生成对应数量的工单
|
||||
4. WHEN 某个样品的工单创建失败 THEN 系统应继续创建其他样品的工单并记录失败信息
|
||||
|
||||
### Requirement 3
|
||||
|
||||
**User Story:** 作为系统管理员,我希望工单能够正确关联样品信息,以便追溯测试对象。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN 系统创建工单 THEN 工单应包含样品的SN号作为工单名称的一部分
|
||||
2. WHEN 系统创建工单 THEN 工单应关联样品ID(test_eut_id字段)
|
||||
3. WHEN 系统创建工单 THEN 工单应关联测试类别ID(test_category_id字段)
|
||||
4. WHEN 系统创建工单 THEN 工单应关联测试单元ID(test_item_id字段)
|
||||
5. WHEN 系统创建工单 THEN 工单应记录创建人信息
|
||||
|
||||
### Requirement 4
|
||||
|
||||
**User Story:** 作为测试人员,我希望只能为特定状态的样品生成工单,以避免重复创建。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN 样品状态为"待测试"(status='0') THEN 系统应允许为该样品生成工单
|
||||
2. WHEN 样品已有关联的工单 THEN 系统应提示用户该样品已生成工单
|
||||
3. WHEN 用户确认为已有工单的样品重新生成 THEN 系统应允许创建新工单
|
||||
4. WHEN 工单生成成功 THEN 系统可选择性地更新样品状态为"测试中"(status='1')
|
||||
|
||||
### Requirement 5
|
||||
|
||||
**User Story:** 作为测试人员,我希望能够在工单生成对话框中输入工单备注,以便记录特殊说明。
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN 用户打开工单生成对话框 THEN 系统应提供工单名称输入框
|
||||
2. WHEN 用户打开工单生成对话框 THEN 系统应提供备注输入框
|
||||
3. WHEN 用户输入工单名称 THEN 该名称应应用于所有生成的工单
|
||||
4. WHEN 用户输入备注 THEN 该备注应应用于所有生成的工单
|
||||
5. WHEN 用户未输入工单名称 THEN 系统应使用默认命名规则(样品SN + 测试类别)
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
# Implementation Plan
|
||||
|
||||
- [x] 1. 创建后端数据模型和VO
|
||||
|
||||
|
||||
- 创建GenerateWorkOrderFromSampleModel请求模型
|
||||
- 创建WorkOrderGenerationResponseModel响应模型
|
||||
- 确保模型支持camelCase和snake_case转换
|
||||
- _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5_
|
||||
|
||||
- [ ] 2. 实现后端服务层逻辑
|
||||
- [x] 2.1 在warehouse_sample_service.py中实现generate_work_orders_from_samples方法
|
||||
|
||||
|
||||
- 验证样品ID列表有效性
|
||||
- 验证测试流程ID有效性
|
||||
- 查询样品信息并验证状态
|
||||
- 获取测试流程的测试类别列表
|
||||
- _Requirements: 1.4, 2.1, 4.1, 4.2_
|
||||
|
||||
- [x] 2.2 实现工单创建逻辑
|
||||
|
||||
- 为每个样品和测试类别组合创建工单
|
||||
- 查询对应的测试单元(test_item)
|
||||
- 查询测试任务(test_job)获取测试人员信息
|
||||
- 构造工单数据并保存
|
||||
- _Requirements: 2.3, 3.2, 3.3, 3.4, 3.5_
|
||||
|
||||
- [x] 2.3 实现样品状态更新逻辑
|
||||
|
||||
- 工单创建成功后更新样品状态为"测试中"
|
||||
- 处理批量更新场景
|
||||
- _Requirements: 4.4_
|
||||
|
||||
- [x] 2.4 实现错误处理和结果汇总
|
||||
|
||||
- 捕获单个样品的创建失败,不影响其他样品
|
||||
- 记录失败原因
|
||||
- 返回成功和失败的统计信息
|
||||
- _Requirements: 2.4_
|
||||
|
||||
- [ ] 2.5 编写服务层单元测试
|
||||
- 测试样品状态验证逻辑
|
||||
- 测试测试流程配置查询
|
||||
- 测试工单数据构造
|
||||
- 测试错误处理逻辑
|
||||
- _Requirements: 1.4, 2.1, 2.2, 2.3, 2.4, 3.1, 3.2, 3.3, 3.4, 3.5, 4.1, 4.2, 4.4_
|
||||
|
||||
- [ ] 3. 实现后端API控制器
|
||||
- [x] 3.1 在warehouse_sample_controller.py中添加generate_work_orders端点
|
||||
|
||||
|
||||
- 定义POST /warehouse/sample/generate_work_orders路由
|
||||
- 接收GenerateWorkOrderFromSampleModel请求
|
||||
- 调用服务层方法
|
||||
- 返回WorkOrderGenerationResponseModel响应
|
||||
- 添加权限验证
|
||||
- _Requirements: 1.2, 1.4_
|
||||
|
||||
- [x] 3.2 添加请求验证
|
||||
|
||||
- 验证sample_ids不为空
|
||||
- 验证test_flow_id有效
|
||||
- 添加适当的错误响应
|
||||
- _Requirements: 1.4_
|
||||
|
||||
- [ ] 3.3 编写控制器单元测试
|
||||
- 测试正常请求处理
|
||||
- 测试参数验证
|
||||
- 测试权限验证
|
||||
- _Requirements: 1.2, 1.4_
|
||||
|
||||
- [ ] 4. 实现前端样品管理页面增强
|
||||
- [x] 4.1 在warehouse/sample/index.vue中添加"生成工单"按钮
|
||||
|
||||
|
||||
- 在操作按钮区域添加"生成工单"按钮
|
||||
- 按钮仅在选中样品时启用
|
||||
- 添加权限控制
|
||||
- _Requirements: 1.1_
|
||||
|
||||
- [x] 4.2 添加工单生成对话框
|
||||
|
||||
- 创建el-dialog组件
|
||||
- 添加测试流程选择下拉框
|
||||
- 添加工单名称输入框
|
||||
- 添加备注输入框
|
||||
- 显示选中的样品数量
|
||||
- _Requirements: 1.2, 5.1, 5.2_
|
||||
|
||||
- [x] 4.3 实现测试流程选择功能
|
||||
|
||||
- 加载测试流程选项列表
|
||||
- 监听测试流程变化
|
||||
- 获取并显示测试流程标签
|
||||
- 使用彩色标签展示测试类别
|
||||
- _Requirements: 1.3, 2.2_
|
||||
|
||||
- [x] 4.4 实现工单生成提交逻辑
|
||||
|
||||
- 收集表单数据
|
||||
- 调用后端API
|
||||
- 处理成功响应(显示成功消息,刷新列表)
|
||||
- 处理失败响应(显示错误详情)
|
||||
- 关闭对话框
|
||||
- _Requirements: 1.4, 1.5_
|
||||
|
||||
- [x] 4.5 添加前端数据字段和方法
|
||||
|
||||
- 添加workOrderDialogVisible状态
|
||||
- 添加workOrderForm表单对象
|
||||
- 添加testFlowOptions选项列表
|
||||
- 添加testFlowTags标签列表
|
||||
- 实现handleGenerateWorkOrder方法
|
||||
- 实现loadTestFlowOptions方法
|
||||
- 实现fetchTestFlowTags方法
|
||||
- 实现submitWorkOrderGeneration方法
|
||||
- _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5_
|
||||
|
||||
- [ ] 4.6 编写前端组件测试
|
||||
- 测试对话框打开和关闭
|
||||
- 测试表单验证
|
||||
- 测试API调用
|
||||
- _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5_
|
||||
|
||||
- [ ] 5. 创建API接口文件
|
||||
- [x] 5.1 在ruoyi-fastapi-frontend/src/api/warehouse/sample.js中添加generateWorkOrders方法
|
||||
|
||||
|
||||
|
||||
- 定义POST请求方法
|
||||
- 配置请求URL和参数
|
||||
- 导出方法供组件使用
|
||||
- _Requirements: 1.4_
|
||||
|
||||
- [ ] 6. 测试和验证
|
||||
- [ ] 6.1 端到端测试
|
||||
- 创建测试样品数据
|
||||
- 测试单个样品生成工单
|
||||
- 测试批量样品生成工单
|
||||
- 验证工单数据正确性
|
||||
- 验证样品状态更新
|
||||
- _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 2.1, 2.2, 2.3, 2.4, 3.1, 3.2, 3.3, 3.4, 3.5, 4.1, 4.2, 4.4_
|
||||
|
||||
- [ ] 6.2 属性测试
|
||||
- **Property 1: 工单创建完整性**
|
||||
- **Validates: Requirements 2.3**
|
||||
|
||||
- [ ] 6.3 属性测试
|
||||
- **Property 2: 样品状态一致性**
|
||||
- **Validates: Requirements 4.4**
|
||||
|
||||
- [ ] 6.4 属性测试
|
||||
- **Property 3: 工单数据关联正确性**
|
||||
- **Validates: Requirements 3.2**
|
||||
|
||||
- [ ] 6.5 属性测试
|
||||
- **Property 4: 测试类别映射正确性**
|
||||
- **Validates: Requirements 2.2**
|
||||
|
||||
- [ ] 6.6 属性测试
|
||||
- **Property 5: 批量操作原子性**
|
||||
- **Validates: Requirements 2.4**
|
||||
|
||||
- [ ] 6.7 属性测试
|
||||
- **Property 6: 工单命名规则一致性**
|
||||
- **Validates: Requirements 5.5**
|
||||
|
||||
- [ ] 7. 错误场景测试
|
||||
- [ ] 7.1 测试样品不存在场景
|
||||
- 提供不存在的样品ID
|
||||
- 验证错误处理
|
||||
- _Requirements: 2.4_
|
||||
|
||||
- [ ] 7.2 测试样品状态不符合场景
|
||||
- 使用非"待测试"状态的样品
|
||||
- 验证被正确拒绝
|
||||
- _Requirements: 4.1, 4.2_
|
||||
|
||||
- [ ] 7.3 测试测试流程不存在场景
|
||||
- 提供不存在的测试流程ID
|
||||
- 验证错误响应
|
||||
- _Requirements: 2.1_
|
||||
|
||||
- [ ] 7.4 测试部分失败场景
|
||||
- 混合有效和无效样品
|
||||
- 验证部分成功的处理
|
||||
- 验证失败信息记录
|
||||
- _Requirements: 2.4_
|
||||
|
||||
- [ ] 8. 最终检查点
|
||||
- 确保所有测试通过
|
||||
- 验证前后端集成正常
|
||||
- 检查用户体验流畅性
|
||||
- 如有问题,询问用户
|
||||
|
|
@ -8,7 +8,8 @@ from module_admin.service.login_service import LoginService
|
|||
from module_admin.service.warehouse_sample_service import WarehouseSampleService
|
||||
from module_admin.entity.vo.warehouse_sample_vo import (
|
||||
WarehouseSamplePageQueryModel, AddWarehouseSampleModel,
|
||||
EditWarehouseSampleModel, DeleteWarehouseSampleModel
|
||||
EditWarehouseSampleModel, DeleteWarehouseSampleModel,
|
||||
GenerateWorkOrderFromSampleModel
|
||||
)
|
||||
from module_admin.entity.vo.user_vo import CurrentUserModel
|
||||
from utils.log_util import logger
|
||||
|
|
@ -100,3 +101,37 @@ async def delete_warehouse_sample(
|
|||
return ResponseUtil.success(msg=result.message)
|
||||
|
||||
|
||||
|
||||
|
||||
@warehouseSampleController.post('/generate_work_orders', dependencies=[Depends(CheckUserInterfaceAuth('warehouse:sample:add'))])
|
||||
@Log(title='样品生成工单', business_type=BusinessType.INSERT)
|
||||
async def generate_work_orders_from_samples(
|
||||
request: Request,
|
||||
generate_request: GenerateWorkOrderFromSampleModel,
|
||||
query_db: AsyncSession = Depends(get_db),
|
||||
current_user: CurrentUserModel = Depends(LoginService.get_current_user)
|
||||
):
|
||||
"""
|
||||
从样品生成工单
|
||||
"""
|
||||
try:
|
||||
result = await WarehouseSampleService.generate_work_orders_from_samples(
|
||||
query_db,
|
||||
generate_request,
|
||||
current_user.user.user_id
|
||||
)
|
||||
|
||||
# 根据结果返回不同的消息
|
||||
if result.failed_count == 0:
|
||||
message = f'成功为 {result.success_count} 个样品生成工单'
|
||||
elif result.success_count == 0:
|
||||
message = f'工单生成失败,{result.failed_count} 个样品处理失败'
|
||||
else:
|
||||
message = f'部分成功:{result.success_count} 个样品成功,{result.failed_count} 个样品失败'
|
||||
|
||||
logger.info(message)
|
||||
return ResponseUtil.success(data=result.model_dump(by_alias=True), msg=message)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f'生成工单失败: {str(e)}')
|
||||
return ResponseUtil.error(msg=f'生成工单失败: {str(e)}')
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from datetime import date, datetime
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic.alias_generators import to_camel
|
||||
from typing import Optional
|
||||
from typing import Optional, List
|
||||
|
||||
|
||||
class WarehouseSampleModel(BaseModel):
|
||||
|
|
@ -84,3 +84,39 @@ class DeleteWarehouseSampleModel(BaseModel):
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
class GenerateWorkOrderFromSampleModel(BaseModel):
|
||||
"""
|
||||
从样品生成工单的请求模型
|
||||
"""
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
sample_ids: List[int] = Field(description='样品ID列表')
|
||||
test_flow_id: int = Field(description='测试流程ID')
|
||||
work_order_name: Optional[str] = Field(default=None, description='工单名称')
|
||||
memo: Optional[str] = Field(default=None, description='备注')
|
||||
force: Optional[bool] = Field(default=False, description='是否强制生成(忽略样品状态检查)')
|
||||
|
||||
|
||||
class FailedSampleInfo(BaseModel):
|
||||
"""
|
||||
失败的样品信息
|
||||
"""
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
sample_id: int = Field(description='样品ID')
|
||||
sample_sn: Optional[str] = Field(default=None, description='样品SN号')
|
||||
reason: str = Field(description='失败原因')
|
||||
|
||||
|
||||
class WorkOrderGenerationResponseModel(BaseModel):
|
||||
"""
|
||||
工单生成响应模型
|
||||
"""
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
success_count: int = Field(description='成功数量')
|
||||
failed_count: int = Field(description='失败数量')
|
||||
work_order_ids: List[int] = Field(default_factory=list, description='生成的工单ID列表')
|
||||
failed_samples: List[FailedSampleInfo] = Field(default_factory=list, description='失败的样品信息')
|
||||
|
|
|
|||
|
|
@ -114,3 +114,249 @@ class WarehouseSampleService:
|
|||
|
||||
|
||||
|
||||
|
||||
@classmethod
|
||||
async def generate_work_orders_from_samples(
|
||||
cls,
|
||||
db: AsyncSession,
|
||||
request_model: 'GenerateWorkOrderFromSampleModel',
|
||||
current_user_id: int
|
||||
) -> 'WorkOrderGenerationResponseModel':
|
||||
"""
|
||||
从样品生成工单
|
||||
|
||||
:param db: 数据库会话
|
||||
:param request_model: 工单生成请求模型
|
||||
:param current_user_id: 当前用户ID
|
||||
:return: 工单生成响应模型
|
||||
"""
|
||||
from module_admin.entity.vo.warehouse_sample_vo import (
|
||||
GenerateWorkOrderFromSampleModel,
|
||||
WorkOrderGenerationResponseModel,
|
||||
FailedSampleInfo
|
||||
)
|
||||
from module_admin.system.entity.do.test_flow_tags_do import TestFlowTags
|
||||
from module_admin.system.entity.do.test_item_do import TestItem
|
||||
from module_admin.system.entity.do.test_job_do import TestJob
|
||||
from module_admin.system.entity.do.test_order_do import TestOrder
|
||||
from module_admin.system.entity.do.test_work_order_do import TestWorkOrder
|
||||
from module_admin.system.entity.do.test_eut_do import TestEut
|
||||
from sqlalchemy import select
|
||||
|
||||
success_count = 0
|
||||
failed_count = 0
|
||||
work_order_ids = []
|
||||
failed_samples = []
|
||||
|
||||
# 1. 验证样品ID列表
|
||||
if not request_model.sample_ids:
|
||||
raise ServiceException(message='样品ID列表不能为空')
|
||||
|
||||
# 2. 验证测试流程ID
|
||||
test_flow_tags_result = await db.execute(
|
||||
select(TestFlowTags.test_category_id)
|
||||
.where(TestFlowTags.test_flow_id == request_model.test_flow_id)
|
||||
)
|
||||
test_flow_tags = test_flow_tags_result.all()
|
||||
|
||||
print(f"DEBUG: 测试流程ID={request_model.test_flow_id}, 找到 {len(test_flow_tags)} 个测试类别")
|
||||
|
||||
if not test_flow_tags:
|
||||
raise ServiceException(message=f'测试流程ID【{request_model.test_flow_id}】不存在或未配置测试类别')
|
||||
|
||||
print(f"DEBUG: 准备创建测试订单")
|
||||
|
||||
# 3. 创建测试订单(用于分组工单)
|
||||
test_order = TestOrder(
|
||||
name=request_model.work_order_name or f'样品工单-{datetime.now().strftime("%Y%m%d%H%M%S")}',
|
||||
creator=current_user_id,
|
||||
create_time=datetime.now(),
|
||||
update_by=current_user_id,
|
||||
update_time=datetime.now(),
|
||||
complate_count=0, # 完成数量初始为0
|
||||
total_count=len(request_model.sample_ids), # 总数量为样品数量
|
||||
state=0, # 待测试
|
||||
memo=request_model.memo
|
||||
)
|
||||
print(f"DEBUG: 测试订单对象创建成功")
|
||||
db.add(test_order)
|
||||
print(f"DEBUG: 测试订单已添加到session")
|
||||
await db.flush() # 获取生成的order_id
|
||||
order_id = test_order.id
|
||||
print(f"DEBUG: 测试订单已保存,ID={order_id}")
|
||||
|
||||
# 4. 为每个样品生成工单
|
||||
for sample_id in request_model.sample_ids:
|
||||
try:
|
||||
# 4.1 查询样品信息
|
||||
sample = await WarehouseSampleDao.get_sample_by_id(db, sample_id)
|
||||
print(f"DEBUG: 处理样品ID={sample_id}, 样品存在={sample is not None}")
|
||||
if not sample:
|
||||
failed_samples.append(FailedSampleInfo(
|
||||
sampleId=sample_id,
|
||||
sampleSn=None,
|
||||
reason='样品不存在'
|
||||
))
|
||||
failed_count += 1
|
||||
continue
|
||||
|
||||
print(f"DEBUG: 样品ID={sample_id}, SN={sample.sample_sn}, 状态={sample.status}")
|
||||
|
||||
# 4.2 验证样品状态(只允许待测试状态的样品,除非force=True)
|
||||
if not request_model.force and sample.status != '0':
|
||||
failed_samples.append(FailedSampleInfo(
|
||||
sampleId=sample_id,
|
||||
sampleSn=sample.sample_sn,
|
||||
reason='已有工单'
|
||||
))
|
||||
failed_count += 1
|
||||
continue
|
||||
|
||||
# 4.3 为样品创建test_eut记录(如果不存在)
|
||||
# 先查询是否已存在相同SN的test_eut
|
||||
test_eut_result = await db.execute(
|
||||
select(TestEut.id)
|
||||
.where(TestEut.sn == sample.sample_sn)
|
||||
.limit(1)
|
||||
)
|
||||
existing_eut = test_eut_result.first()
|
||||
|
||||
if existing_eut:
|
||||
test_eut_id = existing_eut.id
|
||||
print(f"DEBUG: 样品SN={sample.sample_sn}已有test_eut记录,ID={test_eut_id}")
|
||||
else:
|
||||
# 创建新的test_eut记录
|
||||
test_eut = TestEut(
|
||||
test_order_id=order_id,
|
||||
test_flow_id=request_model.test_flow_id,
|
||||
sn=sample.sample_sn,
|
||||
version=sample.hardware_version,
|
||||
memo=sample.remark
|
||||
)
|
||||
db.add(test_eut)
|
||||
await db.flush()
|
||||
test_eut_id = test_eut.id
|
||||
print(f"DEBUG: 为样品SN={sample.sample_sn}创建test_eut记录,ID={test_eut_id}")
|
||||
|
||||
# 4.4 为该样品的每个测试类别创建工单
|
||||
sample_work_order_count = 0
|
||||
for tag in test_flow_tags:
|
||||
test_category_id = tag.test_category_id
|
||||
print(f"DEBUG: 样品ID={sample_id}, 测试类别ID={test_category_id}")
|
||||
|
||||
# 4.4 查询测试单元(根据测试类别和产品类型)
|
||||
# 注意:这里假设样品的sample_model可以映射到eut_type_id
|
||||
# 如果没有eut_type_id,则使用test_category_id查询
|
||||
test_item_result = await db.execute(
|
||||
select(TestItem.id, TestItem.name)
|
||||
.where(TestItem.test_category_id == test_category_id)
|
||||
.limit(1)
|
||||
)
|
||||
test_item = test_item_result.first()
|
||||
|
||||
print(f"DEBUG: 测试类别ID={test_category_id}, 找到测试单元={test_item is not None}")
|
||||
|
||||
if not test_item:
|
||||
# 如果没有找到测试单元,记录警告但继续
|
||||
print(f"WARNING: 测试类别ID={test_category_id} 没有找到测试单元")
|
||||
continue
|
||||
|
||||
# 4.5 查询测试任务获取测试人员信息
|
||||
test_job_result = await db.execute(
|
||||
select(
|
||||
TestJob.id,
|
||||
TestJob.name,
|
||||
TestJob.tester_id,
|
||||
TestJob.reviewer_id,
|
||||
TestJob.second_tester_id,
|
||||
TestJob.third_tester_id
|
||||
).where(TestJob.test_item_id == test_item.id)
|
||||
.limit(1)
|
||||
)
|
||||
test_job = test_job_result.first()
|
||||
|
||||
print(f"DEBUG: 测试单元ID={test_item.id}, 找到测试任务={test_job is not None}")
|
||||
|
||||
# 4.6 构造工单名称
|
||||
if request_model.work_order_name:
|
||||
work_order_name = f"{request_model.work_order_name}-{sample.sample_sn}"
|
||||
else:
|
||||
work_order_name = f"{sample.sample_sn}-{test_item.name if test_item else '测试'}"
|
||||
|
||||
print(f"DEBUG: 准备创建工单, 名称={work_order_name}")
|
||||
|
||||
# 4.7 创建工单(直接使用DO对象,避免Service层的commit)
|
||||
# 注意:某些字段在数据库中定义为NOT NULL,必须提供默认值
|
||||
work_order = TestWorkOrder(
|
||||
name=work_order_name,
|
||||
test_order_id=order_id, # 关联测试订单
|
||||
test_eut_id=test_eut_id, # 关联test_eut表的ID(不是样品ID)
|
||||
test_category_id=test_category_id,
|
||||
test_item_id=test_item.id if test_item else 0, # 如果没有测试单元,使用0
|
||||
creator=current_user_id,
|
||||
create_time=datetime.now(),
|
||||
update_by=current_user_id,
|
||||
update_time=datetime.now(),
|
||||
test_step=1,
|
||||
test_status=0,
|
||||
tester_id=test_job.tester_id if (test_job and test_job.tester_id) else 0,
|
||||
reviewer_id=test_job.reviewer_id if (test_job and test_job.reviewer_id) else 0,
|
||||
second_tester_id=test_job.second_tester_id if (test_job and test_job.second_tester_id) else 0,
|
||||
third_tester_id=test_job.third_tester_id if (test_job and test_job.third_tester_id) else 0,
|
||||
memo=request_model.memo
|
||||
)
|
||||
|
||||
print(f"DEBUG: 工单对象创建成功,准备保存")
|
||||
|
||||
# 保存工单(使用db.add而不是Service,避免中途commit)
|
||||
db.add(work_order)
|
||||
await db.flush() # flush但不commit
|
||||
work_order_id = work_order.id
|
||||
work_order_ids.append(work_order_id) # 添加到工单ID列表
|
||||
print(f"DEBUG: 工单保存成功,ID={work_order_id}")
|
||||
sample_work_order_count += 1
|
||||
|
||||
# 4.8 如果该样品至少创建了一个工单,更新样品状态为"测试中"
|
||||
if sample_work_order_count > 0:
|
||||
# 只有当样品状态为"待测试"时才更新为"测试中"
|
||||
# 如果样品已经是其他状态(如已在测试中),则保持原状态
|
||||
if sample.status == '0':
|
||||
sample.status = '1' # 测试中
|
||||
sample.update_time = datetime.now()
|
||||
await db.flush()
|
||||
success_count += 1
|
||||
else:
|
||||
failed_samples.append(FailedSampleInfo(
|
||||
sampleId=sample_id,
|
||||
sampleSn=sample.sample_sn,
|
||||
reason='未找到匹配的测试单元'
|
||||
))
|
||||
failed_count += 1
|
||||
|
||||
except Exception as e:
|
||||
# 单个样品失败不影响其他样品
|
||||
# 截断错误消息,只保留前200个字符
|
||||
error_msg = str(e)
|
||||
if len(error_msg) > 200:
|
||||
error_msg = error_msg[:200] + '...'
|
||||
|
||||
failed_samples.append(FailedSampleInfo(
|
||||
sampleId=sample_id,
|
||||
sampleSn=None,
|
||||
reason=f'创建工单失败: {error_msg}'
|
||||
))
|
||||
failed_count += 1
|
||||
# 记录完整错误到日志
|
||||
print(f"ERROR: 样品 {sample_id} 生成工单失败: {str(e)}")
|
||||
continue
|
||||
|
||||
# 5. 提交事务
|
||||
await db.commit()
|
||||
|
||||
# 6. 返回结果
|
||||
return WorkOrderGenerationResponseModel(
|
||||
successCount=success_count,
|
||||
failedCount=failed_count,
|
||||
workOrderIds=work_order_ids,
|
||||
failedSamples=failed_samples
|
||||
)
|
||||
|
|
|
|||
|
|
@ -48,3 +48,12 @@ export function delSample(sampleIds) {
|
|||
|
||||
|
||||
|
||||
|
||||
// 从样品生成工单
|
||||
export function generateWorkOrders(data) {
|
||||
return request({
|
||||
url: '/warehouse/sample/generate_work_orders',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,6 +78,17 @@
|
|||
v-hasPermi="['warehouse:sample:add']"
|
||||
>批量添加样品</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="el-icon-s-order"
|
||||
size="mini"
|
||||
:disabled="multiple"
|
||||
@click="handleGenerateWorkOrder"
|
||||
v-hasPermi="['warehouse:sample:add']"
|
||||
>生成工单</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
|
|
@ -278,6 +289,48 @@
|
|||
<el-button @click="batchSampleOpen = false">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 生成工单对话框 -->
|
||||
<el-dialog title="生成工单" :visible.sync="workOrderDialogVisible" width="600px" append-to-body :close-on-click-modal="false">
|
||||
<el-form label-width="120px">
|
||||
<el-form-item label="选中样品数量">
|
||||
<el-tag type="info">{{ workOrderForm.sampleIds.length }} 个样品</el-tag>
|
||||
</el-form-item>
|
||||
<el-form-item label="测试流程" required>
|
||||
<el-select v-model="workOrderForm.testFlowId" placeholder="请选择测试流程" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in testFlowOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<!-- 测试流程标签显示 -->
|
||||
<div v-if="testFlowTags.length" style="margin-top: 10px;">
|
||||
<el-tag
|
||||
v-for="(tag, index) in testFlowTags"
|
||||
:key="tag.test_category_id"
|
||||
:style="{marginRight: '5px', backgroundColor: tagColors[index % tagColors.length], color: 'white'}"
|
||||
>
|
||||
{{ tag.test_category_name || '' }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="工单名称">
|
||||
<el-input v-model="workOrderForm.workOrderName" placeholder="可选,不填则使用默认命名" />
|
||||
<div style="margin-top: 5px; color: #909399; font-size: 12px">
|
||||
<i class="el-icon-info"></i> 提示:默认命名规则为"样品SN-测试类别"
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="workOrderForm.memo" type="textarea" :rows="3" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitWorkOrderGeneration">确 定</el-button>
|
||||
<el-button @click="workOrderDialogVisible = false">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -314,6 +367,21 @@ export default {
|
|||
batchSampleModel: "",
|
||||
// 批量添加硬件版本号
|
||||
batchHardwareVersion: "",
|
||||
// 工单生成对话框
|
||||
workOrderDialogVisible: false,
|
||||
// 工单表单
|
||||
workOrderForm: {
|
||||
sampleIds: [],
|
||||
testFlowId: null,
|
||||
workOrderName: '',
|
||||
memo: ''
|
||||
},
|
||||
// 测试流程选项
|
||||
testFlowOptions: [],
|
||||
// 测试流程标签
|
||||
testFlowTags: [],
|
||||
// 标签颜色数组
|
||||
tagColors: ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C', '#909399', '#C0C4CC'],
|
||||
// 入库单ID(从路由参数获取)
|
||||
receiptId: null,
|
||||
// 入库单号(从路由参数获取)
|
||||
|
|
@ -347,6 +415,16 @@ export default {
|
|||
this.queryParams.receiptId = this.receiptId;
|
||||
}
|
||||
this.getList();
|
||||
this.loadTestFlowOptions();
|
||||
},
|
||||
watch: {
|
||||
'workOrderForm.testFlowId'(newVal) {
|
||||
if (newVal) {
|
||||
this.fetchTestFlowTags(newVal);
|
||||
} else {
|
||||
this.testFlowTags = [];
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 查询样品列表 */
|
||||
|
|
@ -554,6 +632,167 @@ export default {
|
|||
|
||||
// 限制返回最多20项
|
||||
cb(results.slice(0, 20));
|
||||
},
|
||||
/** 打开生成工单对话框 */
|
||||
handleGenerateWorkOrder() {
|
||||
if (this.ids.length === 0) {
|
||||
this.$modal.msgWarning("请先选择样品");
|
||||
return;
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
this.workOrderForm = {
|
||||
sampleIds: [...this.ids],
|
||||
testFlowId: null,
|
||||
workOrderName: '',
|
||||
memo: ''
|
||||
};
|
||||
this.testFlowTags = [];
|
||||
this.workOrderDialogVisible = true;
|
||||
},
|
||||
/** 加载测试流程选项 */
|
||||
async loadTestFlowOptions() {
|
||||
try {
|
||||
const { listTest_flow } = await import("@/api/system/test_eut");
|
||||
const response = await listTest_flow();
|
||||
this.testFlowOptions = response.data.map(item => ({
|
||||
label: item.name,
|
||||
value: item.id
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('加载测试流程失败:', error);
|
||||
}
|
||||
},
|
||||
/** 获取测试流程标签 */
|
||||
async fetchTestFlowTags(flowId) {
|
||||
try {
|
||||
const { getTest_flow_tags_by_flow_id, listTest_category } = await import("@/api/system/test_order");
|
||||
|
||||
// 获取流程标签
|
||||
const tagsResponse = await getTest_flow_tags_by_flow_id(flowId);
|
||||
|
||||
// 获取所有分类
|
||||
const categoryResponse = await listTest_category();
|
||||
const categoryMap = categoryResponse.rows.reduce((map, item) => {
|
||||
map[item.id] = item.name;
|
||||
return map;
|
||||
}, {});
|
||||
|
||||
// 组合标签信息
|
||||
this.testFlowTags = tagsResponse.data.map(tag => ({
|
||||
test_category_id: tag.testCategoryId,
|
||||
test_category_name: categoryMap[tag.testCategoryId] || ''
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('获取测试流程标签失败:', error);
|
||||
this.testFlowTags = [];
|
||||
}
|
||||
},
|
||||
/** 提交工单生成 */
|
||||
async submitWorkOrderGeneration() {
|
||||
// 验证必填项
|
||||
if (!this.workOrderForm.testFlowId) {
|
||||
this.$modal.msgWarning("请选择测试流程");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { generateWorkOrders } = await import("@/api/warehouse/sample");
|
||||
|
||||
// 第一次尝试生成(不强制)
|
||||
const response = await generateWorkOrders(this.workOrderForm);
|
||||
|
||||
if (response.code === 200) {
|
||||
const result = response.data;
|
||||
|
||||
// 如果所有样品都失败,且失败原因都是状态问题,提示用户确认
|
||||
if (result.successCount === 0 && result.failedCount > 0) {
|
||||
const hasStatusIssue = result.failedSamples.some(s =>
|
||||
s.reason.includes('已有工单') || s.reason.includes('状态不允许') || s.reason.includes('如需重复生成请确认')
|
||||
);
|
||||
|
||||
if (hasStatusIssue) {
|
||||
// 显示失败的样品SN列表
|
||||
const failedSns = result.failedSamples
|
||||
.map(s => s.sampleSn || '样品' + s.sampleId)
|
||||
.join('、');
|
||||
|
||||
// 询问用户是否强制生成
|
||||
this.$confirm(
|
||||
`以下样品已有工单:${failedSns},是否重新生成?`,
|
||||
'确认',
|
||||
{
|
||||
confirmButtonText: '继续',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
).then(async () => {
|
||||
// 用户确认,强制生成
|
||||
const forceForm = { ...this.workOrderForm, force: true };
|
||||
const forceResponse = await generateWorkOrders(forceForm);
|
||||
|
||||
if (forceResponse.code === 200) {
|
||||
const forceResult = forceResponse.data;
|
||||
|
||||
// 显示结果消息
|
||||
if (forceResult.failedCount === 0) {
|
||||
this.$modal.msgSuccess(`成功为 ${forceResult.successCount} 个样品生成工单`);
|
||||
} else if (forceResult.successCount === 0) {
|
||||
this.$modal.msgError(`工单生成失败,${forceResult.failedCount} 个样品处理失败`);
|
||||
} else {
|
||||
this.$modal.msgWarning(`部分成功:${forceResult.successCount} 个样品成功,${forceResult.failedCount} 个样品失败`);
|
||||
}
|
||||
|
||||
// 如果还有失败的样品,显示详情
|
||||
if (forceResult.failedSamples && forceResult.failedSamples.length > 0) {
|
||||
const failedInfo = forceResult.failedSamples.map(s =>
|
||||
`样品${s.sampleSn || s.sampleId}: ${s.reason}`
|
||||
).join('\n');
|
||||
console.log('失败详情:\n' + failedInfo);
|
||||
}
|
||||
|
||||
// 关闭对话框并刷新列表
|
||||
this.workOrderDialogVisible = false;
|
||||
this.getList();
|
||||
} else {
|
||||
this.$modal.msgError(forceResponse.msg || '生成工单失败');
|
||||
}
|
||||
}).catch(() => {
|
||||
// 用户取消
|
||||
this.$modal.msgInfo('已取消');
|
||||
});
|
||||
|
||||
return; // 等待用户确认,不继续执行
|
||||
}
|
||||
}
|
||||
|
||||
// 正常情况:显示结果消息
|
||||
if (result.failedCount === 0) {
|
||||
this.$modal.msgSuccess(`成功为 ${result.successCount} 个样品生成工单`);
|
||||
} else if (result.successCount === 0) {
|
||||
this.$modal.msgError(`工单生成失败,${result.failedCount} 个样品处理失败`);
|
||||
} else {
|
||||
this.$modal.msgWarning(`部分成功:${result.successCount} 个样品成功,${result.failedCount} 个样品失败`);
|
||||
}
|
||||
|
||||
// 如果有失败的样品,显示详情
|
||||
if (result.failedSamples && result.failedSamples.length > 0) {
|
||||
const failedInfo = result.failedSamples.map(s =>
|
||||
`样品${s.sampleSn || s.sampleId}: ${s.reason}`
|
||||
).join('\n');
|
||||
console.log('失败详情:\n' + failedInfo);
|
||||
}
|
||||
|
||||
// 关闭对话框并刷新列表
|
||||
this.workOrderDialogVisible = false;
|
||||
this.getList();
|
||||
} else {
|
||||
this.$modal.msgError(response.msg || '生成工单失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('生成工单失败:', error);
|
||||
this.$modal.msgError('生成工单失败: ' + (error.message || '未知错误'));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue