feat(ops): 巡检归属判定异步服务(Task 6)
- InspectionAttributionService + Impl: 归属判定核心逻辑 - 回溯区域最近 COMPLETED 工单,获取 completionSeconds - 与 ops_bus_area.standardDuration 比较判定责任归属 - T_stay >= threshold → 突发状况(2),< threshold → 个人责任(1) - 判定结果回写 inspection_record(lastOrderId, stayDuration, attributionResult) - InspectionRecordServiceImpl: 注入 AttributionService,异步调用含异常兜底 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
package com.viewsh.module.ops.environment.service.inspection;
|
||||
|
||||
/**
|
||||
* 巡检归属判定 Service 接口
|
||||
*
|
||||
* 当巡检结果为不合格时,异步执行归属判定:
|
||||
* 1. 回溯该区域最近一个 COMPLETED 工单
|
||||
* 2. 比较保洁员实际停留时长与区域标准时长
|
||||
* 3. 判定责任归属并回写巡检记录
|
||||
*/
|
||||
public interface InspectionAttributionService {
|
||||
|
||||
/**
|
||||
* 执行归属判定
|
||||
*
|
||||
* @param recordId 巡检记录ID
|
||||
* @param areaId 区域ID
|
||||
*/
|
||||
void determineAttribution(Long recordId, Long areaId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.viewsh.module.ops.environment.service.inspection;
|
||||
|
||||
import com.viewsh.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
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.dal.dataobject.inspection.OpsInspectionRecordDO;
|
||||
import com.viewsh.module.ops.environment.dal.mysql.inspection.OpsInspectionRecordMapper;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 巡检归属判定 Service 实现
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class InspectionAttributionServiceImpl implements InspectionAttributionService {
|
||||
|
||||
/** 归属判定:个人责任(停留时长不足) */
|
||||
private static final int ATTRIBUTION_PERSONAL = 1;
|
||||
/** 归属判定:突发状况(停留时长达标) */
|
||||
private static final int ATTRIBUTION_EMERGENCY = 2;
|
||||
|
||||
@Resource
|
||||
private OpsInspectionRecordMapper inspectionRecordMapper;
|
||||
|
||||
@Resource
|
||||
private OpsOrderMapper opsOrderMapper;
|
||||
|
||||
@Resource
|
||||
private OpsBusAreaMapper opsBusAreaMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void determineAttribution(Long recordId, Long areaId) {
|
||||
// 1. 回溯该区域最近一个 COMPLETED 工单
|
||||
OpsOrderDO lastOrder = opsOrderMapper.selectOne(new LambdaQueryWrapperX<OpsOrderDO>()
|
||||
.eq(OpsOrderDO::getAreaId, areaId)
|
||||
.eq(OpsOrderDO::getStatus, WorkOrderStatusEnum.COMPLETED.getStatus())
|
||||
.orderByDesc(OpsOrderDO::getEndTime)
|
||||
.last("LIMIT 1"));
|
||||
|
||||
if (lastOrder == null) {
|
||||
log.warn("[determineAttribution] 区域 {} 无已完成工单,跳过归属判定: recordId={}", areaId, recordId);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 获取保洁员实际停留时长(秒)
|
||||
Integer stayDurationSeconds = lastOrder.getCompletionSeconds();
|
||||
if (stayDurationSeconds == null) {
|
||||
stayDurationSeconds = 0;
|
||||
}
|
||||
|
||||
// 3. 获取区域标准时长(分钟 → 秒)
|
||||
OpsBusAreaDO area = opsBusAreaMapper.selectById(areaId);
|
||||
if (area == null || area.getStandardDuration() == null) {
|
||||
log.warn("[determineAttribution] 区域 {} 无标准时长配置,跳过归属判定: recordId={}", areaId, recordId);
|
||||
return;
|
||||
}
|
||||
int standardDurationSeconds = area.getStandardDuration() * 60;
|
||||
|
||||
// 4. 判定归属
|
||||
// T_stay >= clean_threshold → 突发状况(保洁员已做到位,不扣分)
|
||||
// T_stay < clean_threshold → 个人责任(保洁时长不足,扣信用分)
|
||||
int attributionResult = stayDurationSeconds >= standardDurationSeconds
|
||||
? ATTRIBUTION_EMERGENCY
|
||||
: ATTRIBUTION_PERSONAL;
|
||||
|
||||
log.info("[determineAttribution] 归属判定完成: recordId={}, areaId={}, lastOrderId={}, " +
|
||||
"stayDuration={}s, standardDuration={}s, result={}",
|
||||
recordId, areaId, lastOrder.getId(),
|
||||
stayDurationSeconds, standardDurationSeconds, attributionResult);
|
||||
|
||||
// 5. 回写巡检记录
|
||||
OpsInspectionRecordDO update = new OpsInspectionRecordDO();
|
||||
update.setId(recordId);
|
||||
update.setLastOrderId(lastOrder.getId());
|
||||
update.setStayDuration(stayDurationSeconds);
|
||||
update.setAttributionResult(attributionResult);
|
||||
inspectionRecordMapper.updateById(update);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,6 +34,9 @@ public class InspectionRecordServiceImpl implements InspectionRecordService {
|
||||
@Resource
|
||||
private OpsInspectionRecordItemMapper inspectionRecordItemMapper;
|
||||
|
||||
@Resource
|
||||
private InspectionAttributionService inspectionAttributionService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long submitInspection(InspectionSubmitReqVO submitReqVO, Long inspectorId) {
|
||||
@@ -72,12 +75,15 @@ public class InspectionRecordServiceImpl implements InspectionRecordService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步触发归属判定(Task 6 实现完整逻辑)
|
||||
* 异步触发归属判定
|
||||
*/
|
||||
@Async
|
||||
public void triggerAttributionAsync(Long recordId, Long areaId) {
|
||||
log.info("[triggerAttributionAsync] 巡检不合格,触发归属判定: recordId={}, areaId={}", recordId, areaId);
|
||||
// TODO: Task 6 实现 InspectionAttributionService 归属判定逻辑
|
||||
try {
|
||||
inspectionAttributionService.determineAttribution(recordId, areaId);
|
||||
} catch (Exception e) {
|
||||
log.error("[triggerAttributionAsync] 归属判定异常: recordId={}, areaId={}", recordId, areaId, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user