package com.testor.common.scheduler;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.testor.biz.sys.dict.data.model.domain.SysDictData;
import com.testor.common.core.constant.Constants;
import com.testor.module.hazard.dao.THazardWorkPlanDao;
import com.testor.module.hazard.dao.THazardWorkPlanExpiredLogDao;
import com.testor.module.hazard.model.domain.THazardWorkPlan;
import com.testor.module.hazard.model.domain.THazardWorkPlanExpiredLog;
import com.testor.module.hazard.model.enums.WorkPlanStatusEnum;
import com.testor.module.sys.model.domian.NewSysOrg;
import com.testor.module.sys.service.NewSysDictDataService;
import com.testor.common.util.DangerousOperationValidator;
import com.testor.module.sys.service.NewSysOrgService;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.runtime.ChangeActivityStateBuilder;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.UserTask;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.task.api.Task;




/**
 * HazardWorkPlanScheduler - 优化版（版本 B）
 *
 * - 字典缓存（可刷新）
 * - 统一过期处理流程（日志 -> 停止流程 -> 更新业务状态）
 * - 更稳健的 flowable 操作（先查再删，删除历史）
 * - 单个 plan 异常捕获，不影响其他计划处理
 *
 * 注意：
 * - 如果你的 DAO 操作需要事务，请在单独需要原子性的私有方法上添加 @Transactional
 * - 如果你希望字典立即生效可调用 refreshDictCache()
 */
@Component
@Slf4j
@ConditionalOnProperty(prefix = "service", name = "taskEnabled", havingValue = "true")
public class HazardWorkPlanScheduler {
    private static final String DICT_ENABLE_ID = "1";
    private static final String DICT_ENABLE_VALUE = "可以执行";

    // 字典id
    private static final String DICT_WORK_TYPE = "42a87414a06a4f57b9d3ffb1907284b4";
    private static final String DICT_WORK_LEVEL = "222842ea3bb4468bbbeaa3856a4f2731";

    @Autowired
    private THazardWorkPlanDao hazardWorkPlanDao;

    @Autowired
    private NewSysDictDataService dictDataService;

    @Autowired
    private THazardWorkPlanExpiredLogDao hazardWorkPlanExpiredLogDao;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private NewSysOrgService orgService;

    // 缓存：key = dictValue, value = SysDictData
    private final Map<String, SysDictData> workTypeCache = new ConcurrentHashMap<>();
    private final Map<String, SysDictData> workLevelCache = new ConcurrentHashMap<>();

    @PostConstruct
    public void init() {
        refreshDictCache();
    }

    /**
     * 定时刷新字典缓存（每 10 分钟刷新一次，保证字典变更能生效）
     */
    //@Scheduled(cron = "0 0/10 * * * ?")
    public void refreshDictCacheScheduled() {
        refreshDictCache();
    }

    private void refreshDictCache() {
        try {
            Map<String, SysDictData> typeMap = dictDataService.getDictId(DICT_WORK_TYPE)
                    .stream()
                    .collect(Collectors.toMap(SysDictData::getDictDataId, Function.identity(), (a, b) -> a));
            Map<String, SysDictData> levelMap = dictDataService.getDictId(DICT_WORK_LEVEL)
                    .stream()
                    .collect(Collectors.toMap(SysDictData::getDictDataId, Function.identity(), (a, b) -> a));

            workTypeCache.clear();
            workTypeCache.putAll(typeMap);

            workLevelCache.clear();
            workLevelCache.putAll(levelMap);

            log.info("HazardWorkPlanScheduler 字典缓存刷新成功：workType={}, workLevel={}",
                    workTypeCache.size(), workLevelCache.size());
        } catch (Exception ex) {
            log.error("刷新字典缓存失败", ex);
        }
    }

    /**
     * 定时刷新开关（每 30 分钟刷新一次，保证开关变更能生效）
     */
    @Scheduled(cron = "0 */30 * * * ?")
    public void refreshSwitch() {
        if (!isSchedulerEnabled()) {
            log.debug("refreshSwitch: 调度被禁用（字典值非 {}）", DICT_ENABLE_VALUE);
            return;
        }

        log.info("开始---refreshSwitch----查询所有危险作业已安全许可申请审批通过后未完成的作业");

        List<THazardWorkPlan> plans;
        try {
            plans = hazardWorkPlanDao.selectHazardLicensePassTime();
        } catch (Exception e) {
            log.error("查询 selectHazardLicensePassTime 失败", e);
            return;
        }

        if (plans == null || plans.isEmpty()) {
            log.info("refreshSwitch: 无需处理的计划");
            return;
        }

        for (THazardWorkPlan plan : plans) {
            try {
                processPlanWithCaches(plan);
            } catch (Exception e) {
                // 单个 plan 异常不影响其他计划
                log.error("处理计划异常，planId={}, code={}", plan == null ? null : plan.getId(),
                        plan == null ? null : plan.getCode(), e);
            }
        }

        log.info("结束---refreshSwitch-----查询所有危险作业已安全许可申请审批通过后未完成的作业");
    }

    /****************************
     * 核心处理函数
     ****************************/

    private void processPlanWithCaches(THazardWorkPlan plan) {
        if (plan == null) {
            return;
        }

        // 从缓存读取字典，减少 DB 查询
        SysDictData workTypeDict = workTypeCache.get(plan.getWorkType());
        SysDictData workLevelDict = workLevelCache.get(plan.getWorkLevel());

        if (workTypeDict == null || workLevelDict == null) {
            log.warn("字典缺失，跳过处理 planId={}, workType={}, workLevel={}",
                    plan.getId(), plan.getWorkType(), plan.getWorkLevel());
            return;
        }

        processHazardWorkPlan(plan, workTypeDict, workLevelDict);
    }

    public void processHazardWorkPlan(THazardWorkPlan plan, SysDictData workTypeDict, SysDictData workLevelDict) {
        if (workTypeDict == null || workLevelDict == null || plan == null) {
            return;
        }

        String workTypeName = workTypeDict.getDictValue();
        String dangerLevelName = workLevelDict.getDictValue();

        DangerousOperationValidator.OperationType operationType =
                DangerousOperationValidator.OperationType.fromDictValue(workTypeName);
        DangerousOperationValidator.DangerLevel dangerLevel =
                DangerousOperationValidator.DangerLevel.fromDictValue(dangerLevelName);

        if (operationType == null || dangerLevel == null) {
            log.warn("无法识别的作业类型或危险等级: {} - {} for planId={}", workTypeName, dangerLevelName, plan.getId());
            return;
        }

        NewSysOrg org = orgService.getOne(new QueryWrapper<NewSysOrg>().eq("org_id", plan.getOrgId()));
        if(org.getParentIds().contains(Constants.DBGK_ORG_ID) || org.getOrgId().equals(Constants.DBGK_ORG_ID)){
            plan.setBl(true);
        }else{
            plan.setBl(false);
        }
        // 计算从审批通过到现在的小时数
        double actualHours = calculateHoursBetween(plan.getHazardLicensePassTime());

        // 验证规则
        String validationResult = DangerousOperationValidator.validateOperation(
                workTypeName, dangerLevelName, actualHours, plan);

        if (!"在安全时间内".equals(validationResult)) {
            handleExpiredOperation(plan, validationResult, operationType, dangerLevel, actualHours);
        } else {
            log.debug("作业在安全时间内, planId={}, hours={}", plan.getId(), actualHours);
        }
    }

    /**
     * 处理过期（核心步骤：记录日志 -> 停止流程 -> 更新业务状态）
     */
    @Transactional
    public void handleExpiredOperation(THazardWorkPlan plan,
                                       String validationResult,
                                       DangerousOperationValidator.OperationType operationType,
                                       DangerousOperationValidator.DangerLevel dangerLevel,
                                       double actualHours) {

        log.warn("作业过期 - planId={}, code={}, type={}, level={}, hours={}, reason={}",
                plan.getId(), plan.getCode(),
                operationType == null ? null : operationType.getName(),
                dangerLevel == null ? null : dangerLevel.getLevel(),
                actualHours, validationResult);

        // 1) 记录过期日志
        try {
            THazardWorkPlanExpiredLog expiredLog = new THazardWorkPlanExpiredLog();
            expiredLog.setPlanId(plan.getId());
            expiredLog.setCode(plan.getCode());
            expiredLog.setWorkType(operationType == null ? null : operationType.getName());
            expiredLog.setDangerLevel(dangerLevel == null ? null : dangerLevel.getLevel());
            expiredLog.setExpiredReason("作业未在安全时间内完成: " + validationResult);
            expiredLog.setBeforeExpiration(plan.getStatus());
            expiredLog.setHazardLicensePassTime(plan.getHazardLicensePassTime());
            expiredLog.setActualDurationHours(BigDecimal.valueOf(actualHours));
            expiredLog.setPlanCreateDate(plan.getCreateDate());
            expiredLog.setCreateBy("system-auto");
            expiredLog.setCreateDate(new Date());

            hazardWorkPlanExpiredLogDao.insert(expiredLog);
        } catch (Exception e) {
            log.error("插入过期日志失败，planId={}", plan.getId(), e);
        }

        //跳转到安全许可证关闭最后一个节点
        jumpToLastNode(plan.getProcessId());

        // 3) 更新业务状态
        try {
            updateOperationStatus(plan, WorkPlanStatusEnum.TIMEOUT_CANCELED.getValue());
        } catch (Exception e) {
            log.error("更新作业状态失败，planId={}, code={}", plan.getId(), plan.getCode(), e);
        }
    }

    /**
     * 检查调度是否启用（通过字典开关）
     */
    private boolean isSchedulerEnabled() {
        try {
            List<SysDictData> dict = dictDataService.getDictId(DICT_ENABLE_ID);
            if (dict != null && !dict.isEmpty()) {
                return DICT_ENABLE_VALUE.equals(dict.get(0).getDictValue());
            }
        } catch (Exception e) {
            log.error("检查调度开关失败", e);
        }
        return false;
    }

    /**
     * 计算从 start 到当前的小时数（对 null 做防护）
     */
    private double calculateHoursBetween(Date start) {
        if (start == null) {
            return 0d;
        }
        long diffMillis = System.currentTimeMillis() - start.getTime();
        return diffMillis / (1000.0 * 60 * 60);
    }

    @Transactional
    public void jumpToLastNode(String processInstanceId) {
        try {
            // 1. 获取流程实例
            ProcessInstance instance = runtimeService.createProcessInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .singleResult();
            if (instance == null) {
                log.warn("流程实例不存在，无法跳转: {}", processInstanceId);
                return;
            }

            // 2. 通过节点名称找到目标节点
            String targetActivityId = findUserTaskByName(instance.getProcessDefinitionId(), "安全许可证关闭");
            if (targetActivityId == null) {
                log.warn("未找到名称为'安全许可证关闭'的节点");
                return;
            }

            log.info("找到目标节点: 安全许可证关闭 (ID: {})", targetActivityId);

            // 3. 获取当前任务的处理人
            List<Task> currentTasks = taskService.createTaskQuery()
                    .processInstanceId(processInstanceId)
                    .list();

            if (currentTasks.isEmpty()) {
                log.warn("当前没有任务，无法获取处理人: {}", processInstanceId);
                return;
            }

            // 获取处理人
            Set<String> assignees = getTaskAssignees(currentTasks, processInstanceId);

            if (assignees.isEmpty()) {
                log.warn("无法确定任务处理人，使用默认处理人");
                // 设置默认处理人
                assignees.add(getDefaultAssignee(processInstanceId));
            }

            log.info("确定的任务处理人: {}", assignees);

            // 4. 获取当前执行流
            List<Execution> executions = runtimeService.createExecutionQuery()
                    .processInstanceId(processInstanceId)
                    .list();

            if (executions.isEmpty()) {
                log.warn("流程无执行节点，不跳转: {}", processInstanceId);
                return;
            }

            // 5. 准备目标节点所需的流程变量
            Map<String, Object> processVariables = prepareProcessVariables(processInstanceId, assignees);

            // 6. 使用变更活动状态API跳转，并设置必要的流程变量
            ChangeActivityStateBuilder builder = runtimeService.createChangeActivityStateBuilder()
                    .processInstanceId(processInstanceId);

            for (Execution execution : executions) {
                if (execution.getActivityId() != null) {
                    builder.moveExecutionToActivityId(execution.getId(), targetActivityId);
                    log.info("将执行流 {} 从活动 {} 跳转到 {}",
                            execution.getId(), execution.getActivityId(), targetActivityId);
                }
            }

            // 设置流程变量，特别是checkUser变量
            builder.processVariables(processVariables);
            builder.changeState();

            // 7. 检查跳转结果
            checkJumpResult(processInstanceId, assignees, targetActivityId);

            log.info("流程 {} 已成功跳转至'安全许可证关闭'节点，处理人: {}",
                    processInstanceId, assignees);

        } catch (Exception e) {
            log.error("跳转到'安全许可证关闭'节点失败，流程实例ID: {}", processInstanceId, e);
            throw new RuntimeException("流程跳转失败", e);
        }
    }

    /**
     * 获取任务处理人（简化版本，不使用IdentityLink）
     */
    private Set<String> getTaskAssignees(List<Task> tasks, String processInstanceId) {
        Set<String> assignees = new HashSet<>();

        for (Task task : tasks) {
            // 1. 首先检查任务是否有直接分配人
            if (task.getAssignee() != null && !task.getAssignee().trim().isEmpty()) {
                assignees.add(task.getAssignee());
                log.info("任务 {} 有分配人: {}", task.getName(), task.getAssignee());
                continue;
            }

            // 2. 如果没有分配人，尝试从任务变量中获取
            String potentialAssignee = findAssigneeFromTaskVariables(task.getId());
            if (potentialAssignee != null) {
                assignees.add(potentialAssignee);
                log.info("从任务变量中找到处理人: {}", potentialAssignee);
                continue;
            }

            // 3. 尝试从流程变量中获取
            potentialAssignee = findAssigneeFromProcessVariables(processInstanceId);
            if (potentialAssignee != null) {
                assignees.add(potentialAssignee);
                log.info("从流程变量中找到处理人: {}", potentialAssignee);
            }

            // 4. 如果以上都没有，记录警告
            if (assignees.isEmpty()) {
                log.warn("任务 {} 没有找到处理人信息", task.getName());
            }
        }

        return assignees;
    }

    /**
     * 从任务变量中查找处理人
     */
    private String findAssigneeFromTaskVariables(String taskId) {
        try {
            Map<String, Object> taskVariables = taskService.getVariables(taskId);
            return findAssigneeInVariables(taskVariables);
        } catch (Exception e) {
            log.warn("从任务变量中查找处理人失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 从流程变量中查找处理人
     */
    private String findAssigneeFromProcessVariables(String processInstanceId) {
        try {
            Map<String, Object> processVariables = runtimeService.getVariables(processInstanceId);
            return findAssigneeInVariables(processVariables);
        } catch (Exception e) {
            log.warn("从流程变量中查找处理人失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 在变量映射中查找处理人
     */
    private String findAssigneeInVariables(Map<String, Object> variables) {
        if (variables == null) {
            return null;
        }

        // 常见的处理人变量名
        String[] potentialAssigneeKeys = {
                "assignee", "userId", "currentUser", "starter",
                "initiator", "checkUser", "approver", "handler"
        };

        for (String key : potentialAssigneeKeys) {
            Object value = variables.get(key);
            if (value != null && !value.toString().trim().isEmpty()) {
                return value.toString();
            }
        }

        return null;
    }

    /**
     * 获取默认处理人
     */
    private String getDefaultAssignee(String processInstanceId) {
        try {
            // 尝试获取流程发起人
            ProcessInstance instance = runtimeService.createProcessInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .singleResult();
            if (instance != null && instance.getStartUserId() != null) {
                return instance.getStartUserId();
            }

            // 尝试从流程变量中获取initiator
            String initiator = (String) runtimeService.getVariable(processInstanceId, "initiator");
            if (initiator != null && !initiator.trim().isEmpty()) {
                return initiator;
            }
        } catch (Exception e) {
            log.warn("获取默认处理人失败: {}", e.getMessage());
        }

        // 返回系统默认用户（请根据实际情况修改）
        return "system_admin";
    }

    /**
     * 准备流程变量，特别是目标节点需要的变量
     */
    private Map<String, Object> prepareProcessVariables(String processInstanceId, Set<String> assignees) {
        Map<String, Object> variables = new HashMap<>();

        // 1. 获取现有的流程变量
        try {
            Map<String, Object> existingVariables = runtimeService.getVariables(processInstanceId);
            if (existingVariables != null) {
                variables.putAll(existingVariables);
            }
        } catch (Exception e) {
            log.warn("获取现有流程变量失败: {}", e.getMessage());
        }

        // 2. 设置目标节点需要的checkUser变量
        if (!assignees.isEmpty()) {
            String checkUser = assignees.iterator().next();
            variables.put("checkUser", checkUser);
            log.info("设置checkUser变量: {}", checkUser);
        } else {
            // 如果没有处理人，设置一个默认值
            String defaultUser = getDefaultAssignee(processInstanceId);
            variables.put("checkUser", defaultUser);
            log.warn("没有找到处理人，使用默认值设置checkUser: {}", defaultUser);
        }

        // 3. 确保其他可能需要的变量存在
        ensureRequiredVariables(variables, processInstanceId);

        log.info("准备的流程变量: {}", variables.keySet());
        return variables;
    }

    /**
     * 确保其他必要变量存在
     */
    private void ensureRequiredVariables(Map<String, Object> variables, String processInstanceId) {
        // 添加其他可能需要的变量
        if (!variables.containsKey("approveResult")) {
            variables.put("approveResult", "agree"); // 默认同意
        }

        // 根据业务需求添加其他必要变量
        if (!variables.containsKey("jumpSource")) {
            variables.put("jumpSource", "auto_jump_to_last_node");
        }
    }

    /**
     * 检查跳转结果
     */
    private void checkJumpResult(String processInstanceId, Set<String> assignees, String expectedActivityId) {
        // 等待流程引擎处理
        try {
            for (int i = 0; i < 3; i++) {
                Thread.sleep(1000);

                List<Task> newTasks = taskService.createTaskQuery()
                        .processInstanceId(processInstanceId)
                        .list();

                if (!newTasks.isEmpty()) {
                    processNewTasks(newTasks, assignees, expectedActivityId);
                    return;
                }
                log.info("第 {} 次检查，尚未发现新任务", i + 1);
            }

            log.warn("跳转后长时间未发现新任务");
            checkExecutionState(processInstanceId, expectedActivityId);

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    /**
     * 处理新生成的任务
     */
    private void processNewTasks(List<Task> newTasks, Set<String> assignees, String expectedActivityId) {
        boolean foundTarget = false;

        for (Task task : newTasks) {
            String taskDefinitionKey = getTaskDefinitionKey(task);
            if (expectedActivityId.equals(taskDefinitionKey)) {
                foundTarget = true;
                log.info("成功跳转到目标节点: {} ({})", task.getName(), taskDefinitionKey);

                // 设置任务分配人（如果任务还没有分配人）
                if (task.getAssignee() == null && !assignees.isEmpty()) {
                    String assignee = assignees.iterator().next();
                    taskService.setAssignee(task.getId(), assignee);
                    log.info("为任务 '{}' 设置处理人: {}", task.getName(), assignee);
                } else if (task.getAssignee() != null) {
                    log.info("任务 '{}' 已有处理人: {}", task.getName(), task.getAssignee());
                }
                break;
            }
        }

        if (!foundTarget) {
            log.warn("跳转未到达预期节点。预期: {}，实际任务: {}",
                    expectedActivityId,
                    newTasks.stream()
                            .map(t -> t.getName() + "(" + getTaskDefinitionKey(t) + ")")
                            .collect(Collectors.toList()));
        }
    }

    /**
     * 检查执行流状态
     */
    private void checkExecutionState(String processInstanceId, String expectedActivityId) {
        try {
            List<Execution> executions = runtimeService.createExecutionQuery()
                    .processInstanceId(processInstanceId)
                    .list();

            log.info("当前执行流状态:");
            for (Execution exec : executions) {
                log.info("执行流: {}, 活动ID: {}", exec.getId(), exec.getActivityId());
            }
        } catch (Exception e) {
            log.warn("检查执行流状态失败: {}", e.getMessage());
        }
    }

    /**
     * 获取任务定义Key
     */
    private String getTaskDefinitionKey(Task task) {
        try {
            // 尝试通过反射获取taskDefinitionKey
            java.lang.reflect.Method method = task.getClass().getMethod("getTaskDefinitionKey");
            Object result = method.invoke(task);
            return result != null ? result.toString() : "unknown";
        } catch (Exception e) {
            // 如果获取失败，返回任务名称作为标识
            return task.getName() != null ? task.getName() : "unknown";
        }
    }

    /**
     * 通过节点名称查找用户任务
     */
    private String findUserTaskByName(String processDefinitionId, String taskName) {
        try {
            BpmnModel model = repositoryService.getBpmnModel(processDefinitionId);
            if (model == null) {
                log.error("无法加载 BPMN 模型: {}", processDefinitionId);
                return null;
            }

            Process process = model.getMainProcess();

            List<UserTask> matchingTasks = process.getFlowElements().stream()
                    .filter(element -> element instanceof UserTask)
                    .map(element -> (UserTask) element)
                    .filter(task -> taskName.equals(task.getName()))
                    .collect(Collectors.toList());

            if (matchingTasks.isEmpty()) {
                log.warn("未找到名称为'{}'的用户任务", taskName);

                // 输出所有用户任务名称，用于调试
                List<UserTask> allUserTasks = process.getFlowElements().stream()
                        .filter(element -> element instanceof UserTask)
                        .map(element -> (UserTask) element)
                        .collect(Collectors.toList());

                if (!allUserTasks.isEmpty()) {
                    log.info("流程中所有用户任务: {}",
                            allUserTasks.stream().map(UserTask::getName).collect(Collectors.toList()));
                }

                return null;
            }

            if (matchingTasks.size() > 1) {
                log.warn("找到多个名称为'{}'的用户任务，将使用第一个", taskName);
                matchingTasks.sort(Comparator.comparing(UserTask::getId));
            }

            UserTask targetTask = matchingTasks.get(0);
            log.info("找到目标任务: {} (ID: {})", targetTask.getName(), targetTask.getId());

            return targetTask.getId();
        } catch (Exception e) {
            log.error("查找用户任务失败: {}", e.getMessage());
            return null;
        }
    }


    /**
     * 停止流程（更稳健的实现：先查询流程实例是否存在，再删除运行时并删除历史）
     * 内部捕获异常，保证调度器稳定运行。
     *
     * 注意：如果你希望在删除历史前保留审计，请删除 historyService.deleteHistoricProcessInstance 调用。
     */
    private void stopFlowableProcess(THazardWorkPlan plan) {
        if (plan == null) {
            return;
        }
        String processInstanceId = plan.getProcessId(); // 保持你当前字段名
        if (StringUtils.isBlank(processInstanceId)) {
            log.debug("stopFlowableProcess: processId 为空，跳过，planId={}", plan.getId());
            return;
        }

        try {
            ProcessInstance pi = runtimeService.createProcessInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .singleResult();

            if (pi == null) {
                log.info("流程实例不存在或已结束，跳过删除，processInstanceId={}, planId={}", processInstanceId, plan.getId());
                // 如果你希望同时清理历史未完成记录，可尝试删除历史（此处不做以免误删）
                try {
                    // 如果确实需要删除历史：historyService.deleteHistoricProcessInstance(processInstanceId);
                } catch (Exception ex) {
                    log.warn("尝试删除历史流程失败，processInstanceId={}", processInstanceId, ex);
                }
                return;
            }

            // 删除运行时流程实例
            runtimeService.deleteProcessInstance(processInstanceId, "系统超时自动取消");

            // 根据业务决定是否删除历史记录；此处删除历史，避免残留（可注释）
            try {
                historyService.deleteHistoricProcessInstance(processInstanceId);
            } catch (Exception e) {
                // 删除历史非关键路径：记录日志即可
                log.warn("删除历史流程实例失败 processInstanceId={}, err={}", processInstanceId, e.getMessage());
            }

            log.info("已停止流程实例 processInstanceId={} (planId={})", processInstanceId, plan.getId());
        } catch (Exception e) {
            log.error("停止流程失败 processInstanceId={}, planId={}", processInstanceId, plan.getId(), e);
        }
    }

    /**
     * 更新业务作业状态（局部更新，避免覆盖其他字段）
     * 如果你希望该更新与过期日志插入为同一事务，请在本方法或调用端加 @Transactional
     */
    private void updateOperationStatus(THazardWorkPlan plan, String newStatus) {
        if (plan == null) {
            return;
        }
        try {
            THazardWorkPlan update = new THazardWorkPlan();
            update.setId(plan.getId());
            update.setWorkStatus(newStatus);
            update.setUpdateDate(new Date()); // 假设字段名为 updateDate；如果不同请改为你项目字段
            hazardWorkPlanDao.updateById(update);
            log.info("更新作业状态成功 planId={}, newStatus={}", plan.getId(), newStatus);
        } catch (Exception e) {
            log.error("更新作业状态失败 planId={}, newStatus={}", plan.getId(), newStatus, e);
            throw e;
        }
    }


}
