feat(ops): 整改工单去重 — 已有活跃工单时升级优先级而非重复创建

- InspectionRectificationServiceImpl 先查区域活跃保洁工单:
  排队中(PENDING/QUEUED)→升级一级优先级;已派发/已到达→静默跳过
- OpsOrderMapper 新增 selectActiveCleanOrder,使用枚举替代硬编码终态
- InspectionAsyncHandler 清理归属判定注释代码,替换为 TODO 标记

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
lzh
2026-03-22 15:00:25 +08:00
parent f213510b03
commit 47c768ec6f
3 changed files with 95 additions and 15 deletions

View File

@@ -27,18 +27,13 @@ public class InspectionAsyncHandler {
*/
@Async("ops-task-executor")
public void handleFailedInspection(Long recordId, Long areaId) {
// 1. 归属判定
try {
inspectionAttributionService.determineAttribution(recordId, areaId);
} catch (Exception e) {
log.error("[handleFailedInspection] 归属判定异常: recordId={}, areaId={}", recordId, areaId, e);
}
// 1. TODO 归属判定:待评分体系完善后启用 inspectionAttributionService.determineAttribution()
// 2. 创建整改工单(不论归属判定结果,不合格即需整改)
// 2. 整改工单:已有活跃工单则升级优先级,否则新建
try {
inspectionRectificationService.createRectificationOrder(recordId, areaId);
} catch (Exception e) {
log.error("[handleFailedInspection] 整改工单创建异常: recordId={}, areaId={}", recordId, areaId, e);
log.error("[handleFailedInspection] 整改工单处理异常: recordId={}, areaId={}", recordId, areaId, e);
}
}

View File

@@ -1,49 +1,113 @@
package com.viewsh.module.ops.environment.service.inspection;
import com.viewsh.module.ops.dal.dataobject.area.OpsBusAreaDO;
import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO;
import com.viewsh.module.ops.dal.mysql.area.OpsBusAreaMapper;
import com.viewsh.module.ops.dal.mysql.workorder.OpsOrderMapper;
import com.viewsh.module.ops.environment.service.cleanorder.dto.CleanOrderAutoCreateReqDTO;
import com.viewsh.module.ops.environment.dal.dataobject.inspection.OpsInspectionRecordDO;
import com.viewsh.module.ops.environment.dal.mysql.inspection.OpsInspectionRecordMapper;
import com.viewsh.module.ops.environment.service.cleanorder.CleanOrderService;
import com.viewsh.module.ops.enums.PriorityEnum;
import com.viewsh.module.ops.enums.SourceTypeEnum;
import com.viewsh.module.ops.enums.WorkOrderStatusEnum;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.util.Set;
/**
* 巡检整改工单 Service 实现
* <p>
* 逻辑与客流阈值触发一致:
* 1. 查该区域有无活跃保洁工单(非终态)
* 2. 有排队中工单PENDING/QUEUED→ 升级优先级一级
* 3. 有已派发/已到达工单 → 保洁员已在处理中,不重复创建
* 4. 无活跃工单 → 新建整改工单
*/
@Service
@Validated
@Slf4j
public class InspectionRectificationServiceImpl implements InspectionRectificationService {
/** 默认整改工单预计时长(分钟) */
private static final int DEFAULT_RECTIFICATION_DURATION_MINUTES = 30;
/** 可升级优先级的状态集合(仅排队中,尚未派发) */
private static final Set<String> UPGRADABLE_STATUSES = Set.of(
WorkOrderStatusEnum.PENDING.getStatus(),
WorkOrderStatusEnum.QUEUED.getStatus()
);
@Resource
private CleanOrderService cleanOrderService;
@Resource
private OpsBusAreaMapper opsBusAreaMapper;
@Resource
private OpsOrderMapper opsOrderMapper;
@Resource
private OpsInspectionRecordMapper inspectionRecordMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createRectificationOrder(Long recordId, Long areaId) {
// 1. 获取区域信息
// 1. 查该区域活跃保洁工单
OpsOrderDO activeOrder = opsOrderMapper.selectActiveCleanOrder(areaId);
if (activeOrder != null) {
return handleExistingOrder(activeOrder, recordId, areaId);
}
// 2. 无活跃工单 → 新建整改工单
return createNewOrder(recordId, areaId);
}
/**
* 已有活跃工单:排队中则升级优先级,已派发/已到达则静默
*/
private Long handleExistingOrder(OpsOrderDO activeOrder, Long recordId, Long areaId) {
Long orderId = activeOrder.getId();
String status = activeOrder.getStatus();
if (UPGRADABLE_STATUSES.contains(status)) {
// 排队中 → 升级优先级一级
PriorityEnum result = cleanOrderService.upgradeOneLevelPriority(
orderId, "巡检不合格自动升级");
if (result != null) {
log.info("[createRectificationOrder] 已有工单优先级升级: recordId={}, orderId={}, newPriority={}",
recordId, orderId, result);
} else {
log.info("[createRectificationOrder] 已有工单已是P0封顶: recordId={}, orderId={}",
recordId, orderId);
}
} else {
// 已派发/已确认/已到达 → 保洁员已在处理中
log.info("[createRectificationOrder] 区域{}保洁员已在处理中(状态:{}),不重复创建: recordId={}, orderId={}",
areaId, status, recordId, orderId);
}
// 回写关联工单ID
updateGeneratedOrderId(recordId, orderId);
return orderId;
}
/**
* 新建整改工单
*/
private Long createNewOrder(Long recordId, Long areaId) {
// 获取区域信息
OpsBusAreaDO area = opsBusAreaMapper.selectById(areaId);
String areaName = (area != null) ? area.getAreaName() : "未知区域";
int expectedDuration = (area != null && area.getStandardDuration() != null)
? area.getStandardDuration() : DEFAULT_RECTIFICATION_DURATION_MINUTES;
// 2. 构建整改工单请求
// 构建请求
CleanOrderAutoCreateReqDTO createReq = new CleanOrderAutoCreateReqDTO();
createReq.setOrderType("CLEAN");
createReq.setSourceType(SourceTypeEnum.INSPECTION.getType());
@@ -55,18 +119,21 @@ public class InspectionRectificationServiceImpl implements InspectionRectificati
createReq.setCleaningType("SPOT");
createReq.setDifficultyLevel(3);
// 3. 调用现有工单引擎创建工单
// 创建工单
Long orderId = cleanOrderService.createAutoCleanOrder(createReq);
log.info("[createRectificationOrder] 整改工单创建成功: recordId={}, areaId={}, orderId={}",
recordId, areaId, orderId);
// 4. 回写 generated_order_id 到巡检记录
// 回写关联工单ID
updateGeneratedOrderId(recordId, orderId);
return orderId;
}
private void updateGeneratedOrderId(Long recordId, Long orderId) {
OpsInspectionRecordDO update = new OpsInspectionRecordDO();
update.setId(recordId);
update.setGeneratedOrderId(orderId);
inspectionRecordMapper.updateById(update);
return orderId;
}
}

View File

@@ -4,6 +4,7 @@ import com.viewsh.framework.mybatis.core.mapper.BaseMapperX;
import com.viewsh.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.viewsh.module.ops.controller.admin.workorder.vo.statistics.*;
import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO;
import com.viewsh.module.ops.enums.WorkOrderStatusEnum;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -74,6 +75,23 @@ public interface OpsOrderMapper extends BaseMapperX<OpsOrderDO> {
.last("LIMIT 1"));
}
/**
* 查询区域内保洁类型的活跃工单(非终态,不限触发来源)
*
* @param areaId 区域ID
* @return 最近创建的活跃保洁工单,不存在返回 null
*/
default OpsOrderDO selectActiveCleanOrder(Long areaId) {
return selectOne(new LambdaQueryWrapperX<OpsOrderDO>()
.eq(OpsOrderDO::getAreaId, areaId)
.eq(OpsOrderDO::getOrderType, "CLEAN")
.notIn(OpsOrderDO::getStatus,
WorkOrderStatusEnum.COMPLETED.getStatus(),
WorkOrderStatusEnum.CANCELLED.getStatus())
.orderByDesc(OpsOrderDO::getCreateTime)
.last("LIMIT 1"));
}
// ==================== 统计聚合查询 ====================
/**