|
|
@ -0,0 +1,197 @@ |
|
|
|
|
|
package com.inspect.reportpush.service; |
|
|
|
|
|
|
|
|
|
|
|
import com.inspect.base.core.constant.RedisConst; |
|
|
|
|
|
import com.inspect.base.core.enums.TaskStatus; |
|
|
|
|
|
import com.inspect.base.core.enums.TaskType; |
|
|
|
|
|
import com.inspect.base.core.utils.StringUtils; |
|
|
|
|
|
import com.inspect.base.redis.service.RedisService; |
|
|
|
|
|
import com.inspect.analysis.domain.ResultAnalysis; |
|
|
|
|
|
import com.inspect.analysis.service.IResultAnalysisService; |
|
|
|
|
|
import com.inspect.reportpush.config.ReportPushProperties; |
|
|
|
|
|
import com.inspect.resultmain.controller.DonghuanReportExportController; |
|
|
|
|
|
import com.inspect.resultmain.domain.PatrolTaskResultMain; |
|
|
|
|
|
import com.inspect.resultmain.service.IPatrolTaskResultMainService; |
|
|
|
|
|
import com.inspect.taskstatus.domain.PatrolTaskStatus; |
|
|
|
|
|
import com.inspect.taskstatus.service.IPatrolTaskStatusService; |
|
|
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
|
|
import org.springframework.scheduling.annotation.Async; |
|
|
|
|
|
import org.springframework.stereotype.Service; |
|
|
|
|
|
|
|
|
|
|
|
import java.util.ArrayList; |
|
|
|
|
|
import java.util.List; |
|
|
|
|
|
import java.util.Map; |
|
|
|
|
|
import java.util.concurrent.TimeUnit; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 任务巡检完成 → 自动归档 + 生成报告(动环 Word) → websocket 推送 |
|
|
|
|
|
* <p> |
|
|
|
|
|
* 由 {@code TaskStatusChangeAspect} 在任务状态变更为「已完成(1)」时异步触发, |
|
|
|
|
|
* 仅处理主任务/普通任务(子任务由主任务统一归档), |
|
|
|
|
|
* 通过 Redis 防重锁避免重复归档。 |
|
|
|
|
|
* |
|
|
|
|
|
* @author inspect |
|
|
|
|
|
*/ |
|
|
|
|
|
@Slf4j |
|
|
|
|
|
@Service |
|
|
|
|
|
public class ReportAutoArchiveService { |
|
|
|
|
|
|
|
|
|
|
|
private final ReportPushProperties properties; |
|
|
|
|
|
private final RedisService redisService; |
|
|
|
|
|
private final IPatrolTaskStatusService patrolTaskStatusService; |
|
|
|
|
|
private final IPatrolTaskResultMainService patrolTaskResultMainService; |
|
|
|
|
|
private final IResultAnalysisService resultAnalysisService; |
|
|
|
|
|
private final DonghuanReportExportController donghuanReportExportController; |
|
|
|
|
|
|
|
|
|
|
|
public ReportAutoArchiveService(ReportPushProperties properties, |
|
|
|
|
|
RedisService redisService, |
|
|
|
|
|
IPatrolTaskStatusService patrolTaskStatusService, |
|
|
|
|
|
IPatrolTaskResultMainService patrolTaskResultMainService, |
|
|
|
|
|
IResultAnalysisService resultAnalysisService, |
|
|
|
|
|
DonghuanReportExportController donghuanReportExportController) { |
|
|
|
|
|
this.properties = properties; |
|
|
|
|
|
this.redisService = redisService; |
|
|
|
|
|
this.patrolTaskStatusService = patrolTaskStatusService; |
|
|
|
|
|
this.patrolTaskResultMainService = patrolTaskResultMainService; |
|
|
|
|
|
this.resultAnalysisService = resultAnalysisService; |
|
|
|
|
|
this.donghuanReportExportController = donghuanReportExportController; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 任务状态变更为已完成时触发(异步)。 |
|
|
|
|
|
* |
|
|
|
|
|
* @param taskStatus 变更后的任务状态(切面入参) |
|
|
|
|
|
*/ |
|
|
|
|
|
@Async |
|
|
|
|
|
public void onTaskCompleted(PatrolTaskStatus taskStatus) { |
|
|
|
|
|
try { |
|
|
|
|
|
if (!properties.isEnabled()) { |
|
|
|
|
|
log.info("[AUTO_ARCHIVE] 未启用 inspect.report-push.enabled,跳过自动归档: taskPatrolledId={}", |
|
|
|
|
|
taskStatus == null ? null : taskStatus.getTaskPatrolledId()); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
if (taskStatus == null || StringUtils.isEmpty(taskStatus.getTaskPatrolledId())) { |
|
|
|
|
|
log.warn("[AUTO_ARCHIVE] 任务状态参数为空,跳过自动归档"); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
// 子任务不单独归档,由主任务统一归档 |
|
|
|
|
|
if (TaskType.SUB.getCode().equals(taskStatus.getTaskType())) { |
|
|
|
|
|
log.debug("[AUTO_ARCHIVE] 子任务不触发归档: {}", taskStatus.getTaskPatrolledId()); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 归档单元:主任务(联合任务)或普通任务自身 |
|
|
|
|
|
PatrolTaskStatus mainTask = patrolTaskStatusService.selectPatrolTaskStatusMain(taskStatus.getTaskPatrolledId()); |
|
|
|
|
|
String archiveTaskPatrolledId = mainTask != null |
|
|
|
|
|
? mainTask.getTaskPatrolledId() : taskStatus.getTaskPatrolledId(); |
|
|
|
|
|
if (StringUtils.isEmpty(archiveTaskPatrolledId)) { |
|
|
|
|
|
log.warn("[AUTO_ARCHIVE] 无法确定归档任务ID,跳过: {}", taskStatus.getTaskPatrolledId()); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 防重锁:同一归档单元只允许一次自动归档 |
|
|
|
|
|
String lockKey = RedisConst.REPORT_PUSH_LOCK + archiveTaskPatrolledId; |
|
|
|
|
|
Boolean locked = redisService.redisTemplate.opsForValue() |
|
|
|
|
|
.setIfAbsent(lockKey, archiveTaskPatrolledId, |
|
|
|
|
|
properties.getLockExpireSeconds(), TimeUnit.SECONDS); |
|
|
|
|
|
if (!Boolean.TRUE.equals(locked)) { |
|
|
|
|
|
log.info("[AUTO_ARCHIVE] 任务 {} 已在归档/推送中(防重锁),跳过", archiveTaskPatrolledId); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
log.info("[AUTO_ARCHIVE] 获取归档防重锁: {}", lockKey); |
|
|
|
|
|
|
|
|
|
|
|
// 等待任务状态事务提交、结果数据落库 |
|
|
|
|
|
long waitMillis = properties.getAfterCommitDelayMillis(); |
|
|
|
|
|
if (waitMillis > 0) { |
|
|
|
|
|
try { |
|
|
|
|
|
Thread.sleep(waitMillis); |
|
|
|
|
|
} catch (InterruptedException e) { |
|
|
|
|
|
Thread.currentThread().interrupt(); |
|
|
|
|
|
releaseLock(lockKey); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 二次校验:任务仍为已完成状态 |
|
|
|
|
|
PatrolTaskStatus freshTask = patrolTaskStatusService.selectPatrolTaskStatusByTaskPatrolledId(archiveTaskPatrolledId); |
|
|
|
|
|
if (freshTask == null || !TaskStatus.DONE.getCode().equals(freshTask.getTaskState())) { |
|
|
|
|
|
log.info("[AUTO_ARCHIVE] 任务 {} 当前状态 {},非已完成,跳过自动归档", |
|
|
|
|
|
archiveTaskPatrolledId, freshTask == null ? "null" : freshTask.getTaskState()); |
|
|
|
|
|
releaseLock(lockKey); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 二次校验:已归档(file_status=1)的任务不重复归档 |
|
|
|
|
|
PatrolTaskResultMain existMain = patrolTaskResultMainService |
|
|
|
|
|
.selectPatrolTaskResultMainByTaskPatrolledId(archiveTaskPatrolledId); |
|
|
|
|
|
if (existMain != null && "1".equals(existMain.getFileStatus())) { |
|
|
|
|
|
log.info("[AUTO_ARCHIVE] 任务 {} 已归档(fileStatus=1),跳过", archiveTaskPatrolledId); |
|
|
|
|
|
releaseLock(lockKey); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 归档范围:主任务 + 其下所有子任务(联合任务) |
|
|
|
|
|
List<String> taskPatrolledIds = new ArrayList<>(); |
|
|
|
|
|
taskPatrolledIds.add(archiveTaskPatrolledId); |
|
|
|
|
|
List<PatrolTaskStatus> subTasks = patrolTaskStatusService.selectPatrolTaskStatusListByMainId(archiveTaskPatrolledId); |
|
|
|
|
|
if (subTasks != null) { |
|
|
|
|
|
for (PatrolTaskStatus subTask : subTasks) { |
|
|
|
|
|
if (StringUtils.isNotEmpty(subTask.getTaskPatrolledId())) { |
|
|
|
|
|
taskPatrolledIds.add(subTask.getTaskPatrolledId()); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 无缺陷不归档(与手动归档一致) |
|
|
|
|
|
// if (!properties.isArchiveWithoutDefect() && !hasDefect(taskPatrolledIds)) { |
|
|
|
|
|
// log.info("[AUTO_ARCHIVE] 任务 {} 无缺陷数据,不归档(archive-without-defect=false)", archiveTaskPatrolledId); |
|
|
|
|
|
// releaseLock(lockKey); |
|
|
|
|
|
// return; |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
// 触发归档(动环 Word 报告),报告生成后由 exportDonghuanReport 回调推送 |
|
|
|
|
|
PatrolTaskResultMain resultMain = new PatrolTaskResultMain(); |
|
|
|
|
|
resultMain.setTaskPatrolledIds(taskPatrolledIds); |
|
|
|
|
|
resultMain.setCheckPerson(properties.getCheckPerson()); |
|
|
|
|
|
resultMain.setTaskResult(properties.getTaskResult()); |
|
|
|
|
|
log.info("[AUTO_ARCHIVE] 触发自动归档: taskPatrolledIds={}, checkPerson={}, taskResult={}", |
|
|
|
|
|
taskPatrolledIds, resultMain.getCheckPerson(), resultMain.getTaskResult()); |
|
|
|
|
|
donghuanReportExportController.updateInfo_donghuan(resultMain); |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
log.error("[AUTO_ARCHIVE] 自动归档异常: taskPatrolledId={}", |
|
|
|
|
|
taskStatus == null ? null : taskStatus.getTaskPatrolledId(), e); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 校验任务(含子任务)是否存在缺陷数据(初筛缺陷 + 大模型缺陷) |
|
|
|
|
|
*/ |
|
|
|
|
|
private boolean hasDefect(List<String> taskPatrolledIds) { |
|
|
|
|
|
try { |
|
|
|
|
|
Map<String, ResultAnalysis> abnormalMap = resultAnalysisService.selectTaskAbnormalDataBatch(taskPatrolledIds); |
|
|
|
|
|
if (abnormalMap == null || abnormalMap.isEmpty()) { |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
int csQxNum = 0, aiQxNum = 0; |
|
|
|
|
|
for (ResultAnalysis analysis : abnormalMap.values()) { |
|
|
|
|
|
if (analysis == null) { |
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
csQxNum += analysis.getCsQxNum(); |
|
|
|
|
|
aiQxNum += analysis.getAiQxNum(); |
|
|
|
|
|
} |
|
|
|
|
|
return csQxNum + aiQxNum > 0; |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
log.error("[AUTO_ARCHIVE] 查询缺陷数据异常,按存在缺陷处理: taskPatrolledIds={}", taskPatrolledIds, e); |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void releaseLock(String lockKey) { |
|
|
|
|
|
try { |
|
|
|
|
|
redisService.deleteObject(lockKey); |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
log.warn("[AUTO_ARCHIVE] 释放防重锁失败: {}", lockKey, e); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |