268 lines
7.8 KiB
Markdown
268 lines
7.8 KiB
Markdown
# 样品状态同步问题修复说明
|
||
|
||
## 问题描述
|
||
删除数据库中的工单数据后,重新生成工单时提示"已有工单",无法创建新工单。
|
||
|
||
## 根本原因
|
||
**与 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
|