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
|