ETest-Vue-FastAPI/样品状态同步问题修复说明.md

268 lines
7.8 KiB
Markdown
Raw Permalink 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.

# 样品状态同步问题修复说明
## 问题描述
删除数据库中的工单数据后,重新生成工单时提示"已有工单",无法创建新工单。
## 根本原因
**与 Redis 无关**问题在于样品表warehouse_sample的状态字段与工单表不同步
1. 样品生成工单时,样品状态从 `'0'`(待测试)更新为 `'1'`(测试中)
2. 删除工单时,只删除了工单数据,**没有重置样品状态**
3. 再次生成工单时,系统检查到样品状态不是 `'0'`,认为"已有工单",拒绝创建
## 相关代码
### 样品状态检查逻辑
**文件**: `warehouse_sample_service.py` 第 162-171 行
```python
# 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
```
### 样品状态更新逻辑
**文件**: `warehouse_sample_service.py` 第 237-243 行
```python
# 4.8 如果该样品至少创建了一个工单,更新样品状态为"测试中"
if sample_work_order_count > 0:
# 只有当样品状态为"待测试"时才更新为"测试中"
if sample.status == '0':
sample.status = '1' # 测试中
sample.update_time = datetime.now()
await db.flush()
```
## 解决方案
### 方案 1重置样品状态快速修复✅ 推荐
执行 SQL 脚本重置样品状态:
```bash
mysql -u root -p your_database < reset_sample_status.sql
```
**SQL 内容**:
```sql
-- 重置所有样品状态为"待测试"
UPDATE warehouse_sample
SET status = '0',
update_time = NOW()
WHERE status != '0';
```
**适用场景**:
- 开发/测试环境
- 需要快速清理数据重新测试
- 确认所有工单都已删除
### 方案 2使用 force 参数(临时方案)
前端在生成工单时传递 `force: true` 参数:
```javascript
// 前端代码
const response = await generateWorkOrderFromSamples({
sampleIds: selectedSampleIds,
testFlowId: testFlowId,
workOrderName: workOrderName,
force: true // 强制生成,跳过状态检查
});
```
**适用场景**:
- 临时需要重新生成工单
- 不想修改数据库数据
- 明确知道样品没有关联的工单
### 方案 3添加级联删除逻辑长期方案
修改工单删除逻辑,删除工单时自动重置样品状态。
#### 3.1 修改工单删除 Service
**文件**: `test_work_order_service.py`
```python
@classmethod
async def delete_test_work_order_services(cls, query_db: AsyncSession, page_object: DeleteTest_work_orderModel):
"""
删除测试工单信息service
"""
if page_object.ids:
id_list = page_object.ids.split(',')
try:
# 1. 查询要删除的工单获取关联的test_eut_id
work_order_ids = [int(id) for id in id_list]
work_orders = await Test_work_orderDao.get_work_orders_by_ids(query_db, work_order_ids)
# 2. 收集所有test_eut_id
test_eut_ids = list(set([wo.test_eut_id for wo in work_orders if wo.test_eut_id]))
# 3. 删除工单
for id in id_list:
await Test_work_orderDao.delete_test_work_order_dao(query_db, Test_work_orderModel(id=id))
# 4. 检查每个test_eut是否还有其他工单
from module_admin.system.entity.do.test_eut_do import TestEut
from module_admin.dao.warehouse_sample_dao import WarehouseSampleDao
from sqlalchemy import select, func
for test_eut_id in test_eut_ids:
# 查询该test_eut是否还有其他工单
remaining_count = (await query_db.execute(
select(func.count(TestWorkOrder.id))
.where(TestWorkOrder.test_eut_id == test_eut_id)
)).scalar()
# 如果没有其他工单了,重置对应样品的状态
if remaining_count == 0:
# 通过test_eut的SN查找样品
test_eut = await query_db.get(TestEut, test_eut_id)
if test_eut:
sample = await WarehouseSampleDao.get_sample_by_sn(query_db, test_eut.sn)
if sample:
sample.status = '0' # 重置为待测试
sample.update_time = datetime.now()
await query_db.commit()
return CrudResponseModel(is_success=True, message='删除成功')
except Exception as e:
await query_db.rollback()
raise e
else:
raise ServiceException(message='传入工单ID为空')
```
#### 3.2 添加 DAO 方法
**文件**: `warehouse_sample_dao.py`
```python
@classmethod
async def get_sample_by_sn(cls, db: AsyncSession, sample_sn: str):
"""
根据样品SN获取样品信息
:param db: orm对象
:param sample_sn: 样品SN
:return: 样品对象
"""
result = await db.execute(
select(WarehouseSample)
.where(WarehouseSample.sample_sn == sample_sn)
)
return result.scalars().first()
```
**适用场景**:
- 生产环境
- 需要保证数据一致性
- 长期维护的系统
## 样品状态说明
| 状态值 | 状态名称 | 说明 |
|--------|----------|------|
| '0' | 待测试 | 样品已入库,未生成工单 |
| '1' | 测试中 | 样品已生成工单,正在测试 |
| '2' | 已完成 | 样品测试完成 |
## 数据一致性检查
### 检查样品状态与工单的一致性
```sql
-- 查找状态为"测试中"但没有工单的样品
SELECT
s.sample_id,
s.sample_sn,
s.status,
COUNT(wo.id) AS work_order_count
FROM warehouse_sample s
LEFT JOIN test_eut te ON s.sample_sn = te.sn
LEFT JOIN test_work_order wo ON te.id = wo.test_eut_id
WHERE s.status = '1' -- 测试中
GROUP BY s.sample_id, s.sample_sn, s.status
HAVING work_order_count = 0;
```
### 修复不一致的数据
```sql
-- 重置没有工单的"测试中"样品
UPDATE warehouse_sample s
LEFT JOIN test_eut te ON s.sample_sn = te.sn
LEFT JOIN test_work_order wo ON te.id = wo.test_eut_id
SET s.status = '0',
s.update_time = NOW()
WHERE s.status = '1'
AND wo.id IS NULL;
```
## 验证步骤
### 1. 执行重置脚本
```bash
mysql -u root -p your_database < reset_sample_status.sql
```
### 2. 检查样品状态
```sql
SELECT status, COUNT(*) FROM warehouse_sample GROUP BY status;
```
预期结果:所有样品状态应该是 `'0'`(待测试)
### 3. 重新生成工单
1. 进入样品管理页面
2. 选择样品
3. 点击"生成工单"
4. 应该能够成功创建工单
### 4. 验证样品状态更新
```sql
-- 查看刚才生成工单的样品状态
SELECT sample_id, sample_sn, status
FROM warehouse_sample
WHERE sample_id IN (选中的样品ID);
```
预期结果:状态应该变为 `'1'`(测试中)
## 常见问题
### Q1: 为什么不是 Redis 缓存问题?
**A**: 代码中没有使用 Redis 缓存样品状态或工单数据。状态检查直接查询数据库。
### Q2: 重启 Redis 有用吗?
**A**: 无用。问题在于数据库中的样品状态字段,与 Redis 无关。
### Q3: 如果只想重置特定样品的状态怎么办?
**A**: 使用带条件的 UPDATE 语句:
```sql
UPDATE warehouse_sample
SET status = '0', update_time = NOW()
WHERE sample_id IN (1, 2, 3); -- 指定样品ID
```
### Q4: 删除工单后为什么不自动重置样品状态?
**A**: 当前代码没有实现级联更新逻辑。建议采用方案 3 添加级联删除逻辑。
## 相关文件
- `warehouse_sample_service.py` - 样品服务(状态检查和更新)
- `test_work_order_service.py` - 工单服务(删除逻辑)
- `warehouse_sample_dao.py` - 样品DAO
- `reset_sample_status.sql` - 重置样品状态脚本 ✅
## 完成时间
2026-01-08 23:45