package com.testor.module.hazard.service.impl;

import cn.afterturn.easypoi.entity.ImageEntity;
import cn.afterturn.easypoi.word.WordExportUtil;
import cn.afterturn.easypoi.word.entity.MyXWPFDocument;
import cn.hutool.core.convert.Convert;
import com.alibaba.fastjson.JSON;
import com.aliyun.core.utils.IOUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.PageList;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.testor.biz.file.model.domain.SysFile;
import com.testor.biz.file.service.SysFileService;
import com.testor.biz.sys.dict.data.model.domain.SysDictData;
import com.testor.biz.sys.dict.data.service.SysDictDataService;
import com.testor.biz.sys.user.model.domain.SysUser;
import com.testor.biz.sys.user.service.SysUserService;
import com.testor.common.constant.RemindConstants;
import com.testor.common.core.constant.Constants;
import com.testor.common.core.utils.StringUtils;
import com.testor.common.core.utils.poi.ExcelUtil;
import com.testor.common.util.BeanConverUtil;
import com.testor.common.util.DangerousOperationValidator;
import com.testor.common.util.DateUtil;
import com.testor.common.util.FileUtil;
import com.testor.module.contractor.ledger.model.domain.TContractorInfo;
import com.testor.module.contractor.ledger.model.domain.TContractorPerson;
import com.testor.module.contractor.ledger.service.TContractorInfoService;
import com.testor.module.contractor.ledger.service.TContractorPersonService;
import com.testor.module.hazard.dao.THazardWorkPlanCheckDao;
import com.testor.module.hazard.dao.THazardWorkPlanDao;
import com.testor.module.hazard.model.domain.*;
import com.testor.module.hazard.model.dto.*;
import com.testor.module.hazard.model.enums.LicenseSignatureTypeEnum;
import com.testor.module.hazard.model.enums.WorkLevelEnum;
import com.testor.module.hazard.model.enums.WorkPlanStatusEnum;
import com.testor.module.hazard.model.enums.WorkTypeEnum;
import com.testor.module.hazard.scheduler.jobs.ReminderJob;
import com.testor.module.hazard.service.*;
import com.testor.module.sys.model.domian.NewSysOrg;
import com.testor.module.sys.model.vo.SysOrgVo;
import com.testor.module.sys.service.NewSysDictDataService;
import com.testor.module.sys.service.NewSysOrgService;
import com.testor.module.wf.service.RuTaskService;
import com.testor.module.wf.vo.HisTasksResponseDTO;
import com.tongtech.tfw.backend.common.biz.constants.BizConstants;
import com.tongtech.tfw.backend.common.context.ContextUtils;
import com.tongtech.tfw.backend.common.models.exceptions.ServiceException;
import com.tongtech.tfw.backend.common.models.response.ResponseInfo;
import com.tongtech.tfw.backend.common.models.supers.SuperServiceImpl;
import com.tongtech.tfw.backend.common.models.sys.UserInfo;
import com.tongtech.tfw.backend.core.helper.IdHelper;
import com.tongtech.tfw.backend.core.helper.ObjectHelper;
import com.tongtech.tfw.backend.core.helper.StringHelper;
import com.tongtech.tfw.workflow.apis.task.controller.WfTaskController;
import com.tongtech.tfw.workflow.apis.task.model.dto.*;
import com.tongtech.tfw.workflow.apis.task.service.ActRuTaskService;
import com.tongtech.tfw.workflow.service.TfwProcessInsService;
import com.tongtech.tfw.workflow.service.dto.ProcessInsStartParams;
import com.tongtech.tfw.workflow.service.dto.ProcessInsStartResult;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.poi.xwpf.usermodel.Document;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFPictureData;
import org.apache.xmlbeans.XmlOptions;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ChangeActivityStateBuilder;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.identitylink.api.IdentityLink;
import org.flowable.idm.api.Group;
import org.flowable.idm.api.User;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 危险作业计划Service业务层处理
 *
 * @author testor-framework
 * @date 2024-12-19 10:39:26
 */
@Slf4j
@Service
public class THazardWorkPlanServiceImpl extends SuperServiceImpl<THazardWorkPlanDao, THazardWorkPlan> implements THazardWorkPlanService {
    private static final String PROJECT_PREFIX = "WXZY";

    @Autowired(required = false)
    private THazardWorkPlanDao tHazardWorkPlanDao;

    @Autowired
    private NewSysOrgService newSysOrgService;

    @Autowired
    private TContractorInfoService tContractorInfoService;

    @Autowired
    private NewSysDictDataService sysDictDataService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private TContractorPersonService tContractorPersonService;
    @Autowired
    private THazardAssessmentService tHazardAssessmentService;
    @Autowired
    private TfwProcessInsService tfwProcessInsService;
    @Autowired
    private THazardAssessmentDetailsService tHazardAssessmentDetailsService;
    @Autowired
    private WfTaskController wfTaskController;
    @Autowired
    private ActRuTaskService actRuTaskService;
    @Autowired
    private THazardConditionConfirmationRecordService tHazardConditionConfirmationRecordService;
    @Autowired
    private SysFileService sysFileService;
    @Autowired
    private Scheduler scheduler;
    @Autowired
    @Lazy
    private THazardConditionConfirmationService tHazardConditionConfirmationService;
    @Autowired
    @Lazy
    private THazardConditionConfirmationDetailsService tHazardConditionConfirmationDetailsService;
    @Autowired
    @Lazy
    private THazardLicenseService tHazardLicenseService;
    @Autowired
    private THazardLicenseAnalysisInfoService tHazardLicenseAnalysisInfoService;
    @Autowired
    private THazardLicenseSignatureRecordService tHazardLicenseSignatureRecordService;
    @Autowired
    private THazardDisclosureService tHazardDisclosureService;
    @Autowired
    private THazardDisclosureDetailsService tHazardDisclosureDetailsService;
    @Autowired
    private RuTaskService ruTaskService;
    @Autowired
    private ResourceLoader resourceLoader;
    @Autowired
    private THazardWorkPlanCheckDao tHazardWorkPlanCheckDao;
    @Autowired
    private TaskService taskService; // 添加TaskService
    @Autowired
    private HistoryService historyService; // 添加HistoryService


    @Override
    public THazardWorkPlanDTO getDetailsById(String id) {
        THazardWorkPlan tHazardWorkPlan = this.getById(id);
        if (tHazardWorkPlan == null) {
            return null;
        }
        THazardWorkPlanDTO tHazardWorkPlanDTO = BeanConverUtil.conver(tHazardWorkPlan, THazardWorkPlanDTO.class);
        //涉及其他作业
        String involvesOtherHazardousWork = tHazardWorkPlan.getInvolvesOtherHazardousWork();
        if (StringUtils.isNotBlank(involvesOtherHazardousWork)) {
            List<String> names = new ArrayList<>();
            String[] split = involvesOtherHazardousWork.split(",");
            for (String dataId : split) {
                SysDictData dictTypeDetail = sysDictDataService.getDictTypeDetail(dataId, null, null);
                if (dictTypeDetail != null) {
                    names.add(dictTypeDetail.getDictValue());
                }
            }
            tHazardWorkPlanDTO.setInvolvesOtherHazardousWorkName(String.join(",", names));
        }
        //作业所在单位
        tHazardWorkPlanDTO.setOperationUnitName(getOrgNamesByOrgIds(tHazardWorkPlanDTO.getOperationUnit()));
        //作业主管单位
        String workSupervisoryUnit = tHazardWorkPlanDTO.getWorkSupervisoryUnit();
        tHazardWorkPlanDTO.setWorkSupervisoryUnitName(getOrgNamesByOrgIds(workSupervisoryUnit));
        //现场负责人
        String workSiteResponsible = tHazardWorkPlanDTO.getWorkSiteResponsible();
        tHazardWorkPlanDTO.setWorkSiteResponsibleName(getUserNamesByUserIds(workSiteResponsible));
        //设置作业单位
        String workUnit = tHazardWorkPlanDTO.getWorkUnit();
        tHazardWorkPlanDTO.setWorkUnitName(getOrgNamesByOrgIds(workUnit));
        ;
        //设置项目负责人
        String projectLeader = tHazardWorkPlanDTO.getProjectLeader();
        tHazardWorkPlanDTO.setProjectLeaderName(getUserNamesByUserIds(projectLeader));
        //监护人
        String guardian = tHazardWorkPlanDTO.getGuardian();
        log.info("guardian-------->"+guardian);
        if(!StringUtils.isEmpty(guardian)){
            tHazardWorkPlanDTO.setGuardianName(getUserNamesByUserIds(guardian));
        }

        //监督人
        String supervisor = tHazardWorkPlanDTO.getSupervisor();
        if(!StringUtils.isEmpty(supervisor)){
            tHazardWorkPlanDTO.setSupervisorName(getUserNamesByUserIds(supervisor));
        }
        //作业人
        String operator = tHazardWorkPlanDTO.getOperator();
        tHazardWorkPlanDTO.setOperatorName(getUserNamesByUserIds(operator));
        //风险分析参与人
        String riskAnalysisParticipant = tHazardWorkPlanDTO.getRiskAnalysisParticipant();
        tHazardWorkPlanDTO.setRiskAnalysisParticipantName(getUserNamesByUserIds(riskAnalysisParticipant));
        //交底人
        String discloser = tHazardWorkPlanDTO.getDiscloser();
        tHazardWorkPlanDTO.setDiscloserName(getUserNamesByUserIds(discloser));
        //申请人
        String createBy = tHazardWorkPlanDTO.getCreateBy();
        SysUser sysUser = sysUserService.getById(createBy);
        if (sysUser != null) {
            tHazardWorkPlanDTO.setCreateByName(sysUser.getUserName());
        }
        //机构名称
        String orgId = tHazardWorkPlanDTO.getOrgId();
        NewSysOrg sysOrg = newSysOrgService.getById(orgId);
        if (sysOrg != null) {
            tHazardWorkPlanDTO.setOrgName(sysOrg.getOrgName());
        }

        //作业方案（含应急方案）
        String workPlanFile = tHazardWorkPlanDTO.getWorkPlanFile();
        if (StringHelper.isNotEmpty(workPlanFile)) {
            List<SysFile> sysFileList = sysFileService.listByIds(Arrays.asList(workPlanFile.split(",")));
            tHazardWorkPlanDTO.setWorkPlanFileInfo(sysFileList);
        }
        tHazardWorkPlanDTO.setBuildWorkStatus(buildWorkStatus(tHazardWorkPlanDTO));
        return tHazardWorkPlanDTO;
    }

    @Override
    public Page<THazardWorkPlanDTO> listEntity(THazardWorkPlanParam param) {
        Long page =
                StringHelper.isEmpty(param.getPage()) ? BizConstants.PAGE : Long.valueOf(param.getPage());
        Long limit =
                StringHelper.isEmpty(param.getLimit()) ? BizConstants.LIMIT : Long.valueOf(param.getLimit());
        Page<THazardWorkPlan> resultPage = new Page<>(page, limit);
        // TODO 根据需求修改查询条件及查询参数
        param.setFlag(true);
        // 默认orgId为当前用户的id
        if(StringUtils.isEmpty(param.getOrgId())){
            param.setFlag(false);
            param.setOrgId(ContextUtils.getLoginUser().getOrgId());
        }
        QueryWrapper<THazardWorkPlan> queryWrapper = this.createQuery(param);
        Page<THazardWorkPlan> tHazardWorkPlanPage = this.page(resultPage, queryWrapper);
        List<THazardWorkPlan> records = tHazardWorkPlanPage.getRecords();
        List<THazardWorkPlanDTO> tHazardWorkPlanDTOS = BeanConverUtil.converList(records, THazardWorkPlanDTO.class);
        for (THazardWorkPlanDTO tHazardWorkPlanDTO : tHazardWorkPlanDTOS) {
            //设置作业单位
            String workUnit = tHazardWorkPlanDTO.getWorkUnit();
            //tHazardWorkPlanDTO.setWorkUnitName(getOrgNamesByOrgId(workUnit));
            tHazardWorkPlanDTO.setWorkUnitName(getOrgNamesByOrgIds(workUnit));
            SysUser sysUser = sysUserService.getById(tHazardWorkPlanDTO.getCreateBy());
            if (sysUser != null) {
                tHazardWorkPlanDTO.setCreateByName(sysUser.getUserName());
            }
            //作业现场负责人
            String workSiteResponsible = tHazardWorkPlanDTO.getWorkSiteResponsible();
            tHazardWorkPlanDTO.setWorkSiteResponsibleName(getUserNamesByUserIds(workSiteResponsible));
            SysOrgVo sysOrgVo = newSysOrgService.lookOrg(tHazardWorkPlanDTO.getOrgId());
            if (sysOrgVo != null) {
                tHazardWorkPlanDTO.setOrgName(sysOrgVo.getSubSectorPath());
            }
            String buildWorkStatus = buildWorkStatus(tHazardWorkPlanDTO);
            tHazardWorkPlanDTO.setBuildWorkStatus(buildWorkStatus);
            if (buildWorkStatus.equals(WorkPlanStatusEnum.TO_BE_CLOSED.getValue())){
                tHazardWorkPlanDTO.setWorkStatus(WorkPlanStatusEnum.TO_BE_CLOSED.getDescription());
            }
            tHazardWorkPlanDTO.setCheckCount(tHazardWorkPlanCheckDao.countByPlanId(tHazardWorkPlanDTO.getId()));

            // 设置当前任务名称
            if (StringUtils.isNotEmpty(tHazardWorkPlanDTO.getProcessId())) {
                String taskName = getCurrentTaskName(tHazardWorkPlanDTO.getProcessId());
                tHazardWorkPlanDTO.setCurrentTaskName(taskName);
            } else {
                tHazardWorkPlanDTO.setCurrentTaskName("未启动");
            }
        }
        Page<THazardWorkPlanDTO> planPage = new Page<>();
        planPage.setRecords(tHazardWorkPlanDTOS);
        planPage.setTotal(tHazardWorkPlanPage.getTotal());
        planPage.setSize(tHazardWorkPlanPage.getSize());
        planPage.setCurrent(tHazardWorkPlanPage.getCurrent());
        return planPage;
    }

    /**
     * 获取当前流程的任务名称
     */
    private String getCurrentTaskName(String processInstanceId) {
        if (StringUtils.isEmpty(processInstanceId)) {
            return "未启动流程";
        }

        try {
            // 查询当前运行中的任务
            List<Task> activeTasks = taskService.createTaskQuery()
                    .processInstanceId(processInstanceId)
                    .list();

            if (activeTasks != null && !activeTasks.isEmpty()) {
                StringBuilder taskNames = new StringBuilder();
                for (Task task : activeTasks) {
                    if (taskNames.length() > 0) {
                        taskNames.append(",");
                    }
                    taskNames.append(task.getName()); // 获取任务名称
                }
                return taskNames.toString();
            }

            // 检查流程是否已结束
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .singleResult();

            if (processInstance == null) {
                return "流程已结束";
            }

            return "等待处理";

        } catch (Exception e) {
            log.error("查询任务名称失败，流程实例ID: {}", processInstanceId, e);
            return "查询失败";
        }
    }

    public String buildWorkStatus(THazardWorkPlanDTO tHazardWorkPlanDTO) {
        if (tHazardWorkPlanDTO.getWorkStatus().equals(WorkPlanStatusEnum.PERMIT.getValue()) && StringUtils.isNotBlank(tHazardWorkPlanDTO.getLicenseStatus())) {
            tHazardWorkPlanDTO.setBuildWorkStatus(WorkPlanStatusEnum.UNDER_APPROVAL.getValue());
        } else if (tHazardWorkPlanDTO.getWorkStatus().equals(WorkPlanStatusEnum.TO_BE_CLOSED.getValue())) {
            tHazardWorkPlanDTO.setBuildWorkStatus(WorkPlanStatusEnum.IN_PROGRESS.getValue());
        } else {
            tHazardWorkPlanDTO.setBuildWorkStatus(tHazardWorkPlanDTO.getWorkStatus());
        }
        return tHazardWorkPlanDTO.getBuildWorkStatus();
    }

    @Override
    public List<THazardWorkPlanDTO> listAllEntity(THazardWorkPlanParam param) {
        // TODO 根据需求修改查询条件及查询参数
        QueryWrapper<THazardWorkPlan> queryWrapper = this.createQuery(param);
        List<THazardWorkPlan> tHazardWorkPlans = this.list(queryWrapper);
        List<THazardWorkPlanDTO> tHazardWorkPlanDTOS = BeanConverUtil.converList(tHazardWorkPlans, THazardWorkPlanDTO.class);
        for (THazardWorkPlanDTO tHazardWorkPlanDTO : tHazardWorkPlanDTOS) {
            //涉及其他作业
            String involvesOtherHazardousWork = tHazardWorkPlanDTO.getInvolvesOtherHazardousWork();
            if (StringUtils.isNotBlank(involvesOtherHazardousWork)) {
                List<String> names = new ArrayList<>();
                String[] split = involvesOtherHazardousWork.split(",");
                for (String dataId : split) {
                    SysDictData dictTypeDetail = sysDictDataService.getDictTypeDetail(dataId, null, null);
                    if (dictTypeDetail != null) {
                        names.add(dictTypeDetail.getDictValue());
                    }
                }
                tHazardWorkPlanDTO.setInvolvesOtherHazardousWorkName(String.join(",", names));
            }
            //作业所在单位
            tHazardWorkPlanDTO.setOperationUnitName(getOrgNamesByOrgId(tHazardWorkPlanDTO.getOperationUnit()));
            //作业主管单位
            String workSupervisoryUnit = tHazardWorkPlanDTO.getWorkSupervisoryUnit();
            tHazardWorkPlanDTO.setWorkSupervisoryUnitName(getOrgNamesByOrgId(workSupervisoryUnit));
            //现场负责人
            String workSiteResponsible = tHazardWorkPlanDTO.getWorkSiteResponsible();
            tHazardWorkPlanDTO.setWorkSiteResponsibleName(getUserNamesByUserIds(workSiteResponsible));
            //设置作业单位
            String workUnit = tHazardWorkPlanDTO.getWorkUnit();
            tHazardWorkPlanDTO.setWorkUnitName(getOrgNamesByOrgId(workUnit));
            ;
            //设置项目负责人
            String projectLeader = tHazardWorkPlanDTO.getProjectLeader();
            tHazardWorkPlanDTO.setProjectLeaderName(getUserNamesByUserIds(projectLeader));
            //监护人
            String guardian = tHazardWorkPlanDTO.getGuardian();
            tHazardWorkPlanDTO.setGuardianName(getUserNamesByUserIds(guardian));
            //监督人
            String supervisor = tHazardWorkPlanDTO.getSupervisor();
            tHazardWorkPlanDTO.setSupervisorName(getUserNamesByUserIds(supervisor));
            //作业人
            String operator = tHazardWorkPlanDTO.getOperator();
            tHazardWorkPlanDTO.setOperatorName(getUserNamesByUserIds(operator));
            //风险分析参与人
            String riskAnalysisParticipant = tHazardWorkPlanDTO.getRiskAnalysisParticipant();
            tHazardWorkPlanDTO.setRiskAnalysisParticipantName(getUserNamesByUserIds(riskAnalysisParticipant));
            //交底人
            String discloser = tHazardWorkPlanDTO.getDiscloser();
            tHazardWorkPlanDTO.setDiscloserName(getUserNamesByUserIds(discloser));

            SysUser sysUser = sysUserService.getById(tHazardWorkPlanDTO.getCreateBy());
            if (sysUser != null) {
                tHazardWorkPlanDTO.setCreateByName(sysUser.getUserName());
            }
            SysOrgVo sysOrgVo = newSysOrgService.lookOrg(tHazardWorkPlanDTO.getOrgId());
            if (sysOrgVo != null) {
                tHazardWorkPlanDTO.setOrgName(sysOrgVo.getSubSectorPath());
            }
        }
        return tHazardWorkPlanDTOS;
    }

    @Override
    public void submit(THazardWorkPlanDTO tHazardWorkPlanDTO) {
        String id = tHazardWorkPlanDTO.getId();
        THazardWorkPlan tHazardWorkPlan = this.getById(id);
        if (tHazardWorkPlan == null) {
            throw new RuntimeException("启动流程失败，未找到作业计划");
        }
        ProcessInsStartParams processInsStartParams = new ProcessInsStartParams();
        processInsStartParams.setBizKey(tHazardWorkPlan.getId());

        String createBy = tHazardWorkPlan.getCreateBy();
        SysUser sysUser = sysUserService.getById(createBy);
        String orgId = sysUser.getOrgId();
        Integer orgTypeByOrgId = newSysOrgService.getOrgTypeByOrgId(orgId);

        String workType = tHazardWorkPlan.getWorkType();
        SysDictData sysDictData = sysDictDataService.getDictTypeDetail(workType, null, null);
        String workTypeValue = sysDictData.getDictKey();

        String processDefinitionKey = "";
        /*if (orgTypeByOrgId == 0) {
            if (WorkTypeEnum.CONFINED_SPACE_WORK.getValue().equals(workTypeValue)) {
                processDefinitionKey = "hazardousTaskConfinedSpace";
            } else if (WorkTypeEnum.CIRCULATING_FUMIGATION.getValue().equals(workTypeValue)) {
                processDefinitionKey = "hazardousTaskStifling";
            }else {
                processDefinitionKey = "hazardousTaskCommon";
            }
        } else if (orgTypeByOrgId == 1) {
            if (WorkTypeEnum.CONFINED_SPACE_WORK.getValue().equals(workTypeValue)) {
                processDefinitionKey = "hazardousTaskBLDeptConfinedSpace";
            } else if (WorkTypeEnum.AMMONIA_UNLOADING.getValue().equals(workTypeValue)){
                processDefinitionKey = "hazardousTaskBLLN2";
            } else {
                processDefinitionKey = "hazardousTaskBLDept";
            }
        } else if (orgTypeByOrgId == 2) {
            if (WorkTypeEnum.AMMONIA_UNLOADING.getValue().equals(workTypeValue)){
                processDefinitionKey = "hazardousTaskBLLN2";
            } else {
                processDefinitionKey = "hazardousTaskBLOrg";
            }
        }*/

        if (orgTypeByOrgId == 0) {
            if (WorkTypeEnum.CONFINED_SPACE_WORK.getValue().equals(workTypeValue)) {
                processDefinitionKey = "hazardousTaskConfinedSpace";
            } else if (WorkTypeEnum.CIRCULATING_FUMIGATION.getValue().equals(workTypeValue)) {
                processDefinitionKey = "hazardousTaskStifling";
            }else {
                processDefinitionKey = "hazardousTaskCommon";
            }
        } else if (orgTypeByOrgId == 1) {
            processDefinitionKey = "BL1";
        } else if (orgTypeByOrgId == 2) {
            processDefinitionKey = "BL2";
        }
        processInsStartParams.setProcessDefinitionKey(processDefinitionKey);
        processInsStartParams.setOrgId(tHazardWorkPlan.getOrgId());
        processInsStartParams.setUserId(tHazardWorkPlan.getCreateBy());
        Map<String, Object> varMap = new HashMap<>();
        processInsStartParams.setVariables(varMap);
        ProcessInsStartResult processInsStartResult = tfwProcessInsService.startProcessInsByKey(processInsStartParams);
        String code = processInsStartResult.getCode();
        tHazardWorkPlan.setWorkStatus(WorkPlanStatusEnum.RISK_ANALYSIS.getValue());
        tHazardWorkPlan.setProcessId(code);
        this.updateById(tHazardWorkPlan);
        HisTasksRequest hisTasksRequest = new HisTasksRequest();
        hisTasksRequest.setProcessInstanceId(processInsStartResult.getCode());
        List<HisTasksResponse> data = wfTaskController.getHistoryByInstanceId(hisTasksRequest).getData().getData();
        List<HisTasksResponse> filteredTasks = data.stream()
                .filter(task -> "基本信息".equals(task.getTaskName()))
                .collect(Collectors.toList());
        HisTasksResponse hisTasksResponse = filteredTasks.get(0);
        CompleteTask completeTask = new CompleteTask();
        completeTask.setOrgId(tHazardWorkPlan.getOrgId());
        completeTask.setUserId(tHazardWorkPlan.getCreateBy());
        completeTask.setBizId(tHazardWorkPlan.getId());
        completeTask.setTaskId(hisTasksResponse.getTaskId());
        completeTask.setLocalScope(true);
        Map<String, Object> vars = new HashMap<>();
        vars.put("workStatus", WorkPlanStatusEnum.RISK_ANALYSIS.getValue());
        vars.put("licenseStatus", "99");
        vars.put("workSiteResponsible", getSubstringAfterDash(tHazardWorkPlan.getWorkSiteResponsible()));
        vars.put("bizId", id);
        vars.put("taskId", hisTasksResponse.getTaskId());
        completeTask.setVars(vars);
        wfTaskController.completeTask(completeTask);
    }

    /**
     * 初始化数据到 安全风险分析、条件确认表
     *
     * @param tHazardWorkPlan
     */
    void initData(THazardWorkPlan tHazardWorkPlan) {
        String workTypes = getWorkTypes(tHazardWorkPlan.getWorkType(), tHazardWorkPlan.getInvolvesOtherHazardousWork());
        //安全风险分析
        tHazardAssessmentService.initData(workTypes, tHazardWorkPlan);
    }


    /**
     * 获取工作类型
     *
     * @param workType                   主工作类型
     * @param involvesOtherHazardousWork 涉及工作类型
     * @return
     */
    public String getWorkTypes(String workType, String involvesOtherHazardousWork) {
        StringBuilder workTypes = new StringBuilder(workType);
        if (StringUtils.isNotBlank(involvesOtherHazardousWork)) {
            String[] involvesOtherHazardousWorks = involvesOtherHazardousWork.split(",");
            for (String otherHazardousWork : involvesOtherHazardousWorks) {
                SysDictData involvesOtherHazardousWorkDict = sysDictDataService.getDictTypeDetail(otherHazardousWork, null, null);
                if (involvesOtherHazardousWorkDict != null) {
                    String dictKey = involvesOtherHazardousWorkDict.getDictKey();
                    //如果不等于不涉及
                    if (!WorkTypeEnum.NOT_INVOLVED.getValue().equals(dictKey)) {
                        workTypes.append(",").append(otherHazardousWork);
                    }
                }
            }
        }
        return String.valueOf(workTypes);
    }


    @Override
    public THazardWorkPlan addEntity(THazardWorkPlan addRequest) {
        String id32bit = IdHelper.getId32bit();
        addRequest.setId(id32bit);
        addRequest.setCode(generateProjectCode());
        String escalatedLevel = getEscalatedLevel(addRequest.getWorkLevel(), addRequest.getIsEscalated());
        addRequest.setEscalatedLevel(escalatedLevel);
        this.save(addRequest);
        return addRequest;
    }

    @Override
    public void completeProcess(THazardWorkPlanDTO tHazardWorkPlanDTO) {
        String id = tHazardWorkPlanDTO.getId();
        THazardWorkPlan tHazardWorkPlan = this.getById(id);
        String workStatus = tHazardWorkPlan.getWorkStatus();
        tHazardWorkPlan.setIsSubmit(tHazardWorkPlanDTO.getIsSubmit());
        Integer batch = tHazardWorkPlanDTO.getBatch();
        if (batch == null) {
            batch = 1;
        }
        tHazardWorkPlan.setBatch(batch);
        Map<String, Object> map = Optional.ofNullable(tHazardWorkPlanDTO.getMap()).orElse(new HashMap<>());
        map.put("currentWorkStatus", workStatus);
        //不驳回
        map.put("rejected", "0");
        String operationUnit = tHazardWorkPlan.getOperationUnit();
        String workSupervisoryUnit = tHazardWorkPlan.getWorkSupervisoryUnit();
        //如果作业所在单位和作业主管单位一致
        if (operationUnit.equals(workSupervisoryUnit)) {
            map.put("sameOrganization", "1");
        } else {
            map.put("sameOrganization", "0");
        }
        //如果为风险分析
        if (WorkPlanStatusEnum.RISK_ANALYSIS.getValue().equals(workStatus)) {
            //流转到安全条件确认
            map.put("workSiteResponsible", getSubstringAfterDash(tHazardWorkPlan.getWorkSiteResponsible()));
            executionWorkProcess(id, tHazardWorkPlanDTO.getTaskId(), WorkPlanStatusEnum.CONDITION_CONFIRMATION.getValue(), map, tHazardWorkPlan);
        }
        //如果为安全条件确认
        if (WorkPlanStatusEnum.CONDITION_CONFIRMATION.getValue().equals(workStatus)) {
            //流转到安全许可证申请
            map.put("workSiteResponsible", getSubstringAfterDash(tHazardWorkPlan.getWorkSiteResponsible()));
            executionWorkProcess(id, tHazardWorkPlanDTO.getTaskId(), WorkPlanStatusEnum.PERMIT.getValue(), map, tHazardWorkPlan);
        }

        //如果为安全许可证申请
        if (WorkPlanStatusEnum.PERMIT.getValue().equals(workStatus) || "99".equals(workStatus)) {
            //更新实际开始时间
            if (null != id) {
                tHazardWorkPlanDao.updateActualStartTime(id);
            }
            SysDictData escalatedLevel = sysDictDataService.getDictTypeDetail(tHazardWorkPlan.getEscalatedLevel(), null, null);
            String escalatedLevelValue = escalatedLevel.getDictKey();
            map.put("level", escalatedLevelValue);
            if (StringUtils.isNotBlank(tHazardWorkPlanDTO.getIsOnSiteConfirmation())) {
                map.put("isOnSiteConfirmation", tHazardWorkPlanDTO.getIsOnSiteConfirmation());
            }
            if (StringUtils.isNotBlank(tHazardWorkPlanDTO.getSiteConfirmation())) {
                map.put("siteConfirmation", tHazardWorkPlanDTO.getSiteConfirmation());
            }
            executionWorkProcess(id, tHazardWorkPlanDTO.getTaskId(), WorkPlanStatusEnum.TECHNICAL_EXPLANATION.getValue(), map, tHazardWorkPlan);
        }

        //如果为技术交底
        if (WorkPlanStatusEnum.TECHNICAL_EXPLANATION.getValue().equals(workStatus)) {
            //流转到安全许可证关闭
            executionWorkProcess(id, tHazardWorkPlanDTO.getTaskId(), WorkPlanStatusEnum.TO_BE_CLOSED.getValue(), tHazardWorkPlanDTO.getMap(), tHazardWorkPlan);

            THazardWorkPlan updWorkPlan = new THazardWorkPlan();
            updWorkPlan.setId(id);
            //updWorkPlan.setHazardLicensePassTime(new Date());
            this.updateById(updWorkPlan);
        }

        //如果为安全许可证关闭
        if (WorkPlanStatusEnum.TO_BE_CLOSED.getValue().equals(workStatus) || WorkPlanStatusEnum.TIMEOUT_CANCELED.getValue().equals(workStatus)) {
            //关闭流程
            executionWorkProcess(id, tHazardWorkPlanDTO.getTaskId(), WorkPlanStatusEnum.CLOSED.getValue(), tHazardWorkPlanDTO.getMap(), tHazardWorkPlan);

            THazardWorkPlan updWorkPlan = new THazardWorkPlan();
            updWorkPlan.setId(id);

            Date scheduledEndTime = tHazardWorkPlan.getScheduledEndTime();
            if(WorkPlanStatusEnum.TIMEOUT_CANCELED.getValue().equals(workStatus)){
                updWorkPlan.setWorkStatus(WorkPlanStatusEnum.TIMEOUT_CANCELED.getValue());
            }else{
                //如果计划结束时间在当前时间之后
                if (scheduledEndTime.after(new Date())) {
                    //更新状态 取消状态
                    updWorkPlan.setWorkStatus(WorkPlanStatusEnum.CLOSED.getValue());
                } else {
                    //更新状态 超时
                    updWorkPlan.setWorkStatus(WorkPlanStatusEnum.OVERDUE.getValue());
                }
            }

            //更新结束时间
            updWorkPlan.setActualEndTime(new Date());
            this.updateById(updWorkPlan);

            //关闭子级流程
            CompleteTask completeTask = new CompleteTask();
            completeTask.setBizId(id);
            Map<String, Object> vars = new HashMap<>();
            vars.put("workStatus", WorkPlanStatusEnum.CANCELED.getValue());
            vars.put("opinion", "作业关闭");
            completeTask.setVars(vars);
            cancelChildTasks(completeTask);
        }
    }


    public void executionWorkProcess(String bizId, String taskId, String processStatus, Map<String, Object> map, THazardWorkPlan tHazardWorkPlan) {
        CompleteTask completeTask = new CompleteTask();
        completeTask.setBizId(bizId);
        completeTask.setTaskId(taskId);
        Map<String, Object> vars = new HashMap<>();
        vars.put("currentWorkerStatus", tHazardWorkPlan.getWorkStatus());
        vars.put("workStatus", processStatus);
        if (map != null) {
            vars.putAll(map);
        }
        completeTask.setVars(vars);
        //流程提交前校验
        workProcessValidation(tHazardWorkPlan, processStatus, vars, taskId);
        wfTaskController.completeTask(completeTask).getData().getResult();
    }

    /**
     * 作业流程提交前校验
     *
     * @param tHazardWorkPlan
     * @param vars
     */
    private void workProcessValidation(THazardWorkPlan tHazardWorkPlan, String processStatus, Map<String, Object> vars, String taskId) {
        SysDictData escalatedLevel = sysDictDataService.getDictTypeDetail(tHazardWorkPlan.getEscalatedLevel(), null, null);
        String escalatedLevelValue = escalatedLevel.getDictKey();
        String isSubmit = tHazardWorkPlan.getIsSubmit();
        if (!"1".equals(isSubmit)) {
            String taskName = "";
            //不为现场确认
            vars.put("isOnSiteConfirmation", "0");
            log.info("危险作业流程--workProcessValidation-nextNameTasks-参数，taskId:{}，vars:{}", taskId, JSON.toJSONString(vars));
            List<String> nextNameTasks = ruTaskService.getNextNameTasks(taskId, vars);
            log.info("危险作业流程--workProcessValidation-nextNameTasks:{}", JSON.toJSONString(nextNameTasks));
            if (nextNameTasks != null && !nextNameTasks.isEmpty()) {
                taskName = nextNameTasks.get(0);
            }
            throw new ServiceException(new ResponseInfo(200, taskName));
        }
        String workStatus = tHazardWorkPlan.getWorkStatus();
        String workTypes = getWorkTypes(tHazardWorkPlan.getWorkType(), tHazardWorkPlan.getInvolvesOtherHazardousWork());
        log.info("危险作业流程--workProcessValidation-workTypes:{}，tHazardWorkPlan:{}", workTypes, JSON.toJSONString(tHazardWorkPlan));
        String[] workTypesArray = workTypes.split(",");
        int workTypesArrayLength = workTypesArray.length;

        //如果为风险分析
        if (workStatus.equals(WorkPlanStatusEnum.RISK_ANALYSIS.getValue())) {
            List<THazardAssessment> tHazardAssessments = tHazardAssessmentService.list(new QueryWrapper<THazardAssessment>().eq(THazardAssessment.PLAN_ID, tHazardWorkPlan.getId())
                    .isNotNull(THazardAssessment.PARTICIPANT_SIGNATURE)
                    .isNotNull(THazardAssessment.WORK_SITE_RESPONSIBLE_SIGNATURE)
                    //todo 生产环境数据库设置了 ora_input_emptystr_isnull 为on 去除空字符校验
                    //.ne(THazardAssessment.PARTICIPANT_SIGNATURE, "")
                    //.ne(THazardAssessment.WORK_SITE_RESPONSIBLE_SIGNATURE, "")
            );

            log.info("危险作业流程--workProcessValidation---tHazardAssessments:{}", JSON.toJSONString(tHazardAssessments));
            log.info("危险作业流程--workProcessValidation---tHazardAssessments.size() < workTypesArrayLength :{},{}", tHazardAssessments.size(), workTypesArrayLength);
            if (tHazardAssessments.size() < workTypesArrayLength) {
                throw new ServiceException(new ResponseInfo(200, "涉及其他类型的风险分析未签字[serviceError]"));
            }
        }
        //如果为条件确认
        else if (workStatus.equals(WorkPlanStatusEnum.CONDITION_CONFIRMATION.getValue())) {
            List<String> dangeWorkTypes = sysDictDataService.getDictIdsByParentKeyAndType(WorkTypeEnum.ENTRY_EXIT_WAREHOUSE.getValue(), "dange_work_type");
            //添加熏蒸作业
            String dangeWorkType = sysDictDataService.getDictIdByKey("dange_work_type", WorkTypeEnum.FUMIGATION_WORK.getValue());
            if (StringUtils.isNotBlank(dangeWorkType)) {
                dangeWorkTypes.add(dangeWorkType);
            }
            //判断当前是否为 较大和高风险
            boolean isHighRisk = WorkLevelEnum.MEDIUM_RISK.getValue().equals(escalatedLevelValue) || WorkLevelEnum.HIGH_RISK.getValue().equals(escalatedLevelValue);

            List<THazardConditionConfirmation> tHazardConditionConfirmations = new ArrayList<>();

            //东北港口新流程图安全管理部门为空,防止orgId为空的时候无流程可走,默认走之前统一流程
            boolean flag = false;
            if(null != tHazardWorkPlan.getOrgId()){
                NewSysOrg sysOrg = newSysOrgService.getById(tHazardWorkPlan.getOrgId());
                log.info("危险作业流程--workProcessValidation---sysOrg:{}", JSON.toJSONString(sysOrg));
                if(null != sysOrg){
                    if(null != sysOrg.getParentIds()){
                        if(sysOrg.getParentIds().contains(Constants.DBGK_ORG_ID) || sysOrg.getOrgId().equals(Constants.DBGK_ORG_ID)){
                            log.info("危险作业流程--workProcessValidation---sysOrg.getParentIds().contains(Constants.DBGK_ORG_ID) || sysOrg.getOrgId().equals(Constants.DBGK_ORG_ID)):{}", sysOrg.getParentIds().contains(Constants.DBGK_ORG_ID) || sysOrg.getOrgId().equals(Constants.DBGK_ORG_ID));
                            tHazardConditionConfirmations = tHazardConditionConfirmationService.list(new QueryWrapper<THazardConditionConfirmation>().eq(THazardConditionConfirmation.PLAN_ID, tHazardWorkPlan.getId())
                                    .eq(THazardConditionConfirmation.BATCH, tHazardWorkPlan.getBatch())
                                    .and(wrapper -> {
                                        wrapper.in(THazardConditionConfirmation.WORK_TYPE_ID, dangeWorkTypes) //如果是进出仓
                                                .isNotNull(THazardConditionConfirmation.WORK_SITE_RESPONSIBLE_SIGNATURE) //作业现场负责人不能为空
                                                //.ne(THazardConditionConfirmation.WORK_SITE_RESPONSIBLE_SIGNATURE, "")
                                                .or()
                                                .notIn(THazardConditionConfirmation.WORK_TYPE_ID, dangeWorkTypes) //如果不为进出仓
                                                //.ne(isHighRisk, THazardConditionConfirmation.DEPARTMENT_SIGNATURE, "")
                                                .isNotNull(THazardConditionConfirmation.WORK_SITE_RESPONSIBLE_SIGNATURE); //作业现场负责人不能为空
                                        //.ne(THazardConditionConfirmation.WORK_SITE_RESPONSIBLE_SIGNATURE, "");
                                    })
                            );
                            flag = true;
                        }
                    }
                }
            }

            log.info("危险作业流程--workProcessValidation---tHazardConditionConfirmations:{}", JSON.toJSONString(tHazardConditionConfirmations));
            log.info("危险作业流程--workProcessValidation---flag {}", flag);
            if(!flag){
                tHazardConditionConfirmations = tHazardConditionConfirmationService.list(new QueryWrapper<THazardConditionConfirmation>().eq(THazardConditionConfirmation.PLAN_ID, tHazardWorkPlan.getId())
                        .eq(THazardConditionConfirmation.BATCH, tHazardWorkPlan.getBatch())
                        .and(wrapper -> {
                            wrapper.in(THazardConditionConfirmation.WORK_TYPE_ID, dangeWorkTypes) //如果是进出仓
                                    .isNotNull(THazardConditionConfirmation.WORK_SITE_RESPONSIBLE_SIGNATURE) //作业现场负责人不能为空
                                    //.ne(THazardConditionConfirmation.WORK_SITE_RESPONSIBLE_SIGNATURE, "")
                                    .or()
                                    .notIn(THazardConditionConfirmation.WORK_TYPE_ID, dangeWorkTypes) //如果不为进出仓
                                    .isNotNull(isHighRisk, THazardConditionConfirmation.DEPARTMENT_SIGNATURE) //安全管理部门不能为空
                                    //.ne(isHighRisk, THazardConditionConfirmation.DEPARTMENT_SIGNATURE, "")
                                    .isNotNull(THazardConditionConfirmation.WORK_SITE_RESPONSIBLE_SIGNATURE); //作业现场负责人不能为空
                            //.ne(THazardConditionConfirmation.WORK_SITE_RESPONSIBLE_SIGNATURE, "");
                        })
                );
            }

            log.info("危险作业流程--workProcessValidation---tHazardConditionConfirmations.size() < workTypesArrayLength :{},{}", tHazardConditionConfirmations.size(), workTypesArrayLength);
            if (tHazardConditionConfirmations.size() < workTypesArrayLength) {
                throw new ServiceException(new ResponseInfo(200, "涉及其他类型的条件确认未签字[serviceError]"));
            }
        }
        //如果为技术交底
        else if (workStatus.equals(WorkPlanStatusEnum.TECHNICAL_EXPLANATION.getValue())) {
            List<THazardDisclosure> tHazardTechnicalDisclosures = tHazardDisclosureService.list(new QueryWrapper<THazardDisclosure>()
                    .eq(THazardDisclosure.PLAN_ID, tHazardWorkPlan.getId())
                    .eq(THazardDisclosure.BATH, tHazardWorkPlan.getBatch())
                    .isNotNull(THazardDisclosure.DISCLOSURE_PERSON_SIGN)
                    .isNotNull(THazardDisclosure.RECIPIENT_SIGNATURE)
                    //.ne(THazardDisclosure.DISCLOSURE_PERSON_SIGN, "")
                    //.ne(THazardDisclosure.RECIPIENT_SIGNATURE, "")
            );
            if (tHazardTechnicalDisclosures.size() < workTypesArrayLength) {
                throw new ServiceException(new ResponseInfo(200, "涉及其他类型的技术交底未签字[serviceError]"));
            }
        }

    }


    public String generateProjectCode() {
        String orgId = ContextUtils.getLoginUser().getOrgId();

        // 获取当前日期
        String currentDate = new SimpleDateFormat("yyyyMMdd").format(new Date());

        // 查询当前日期下最大的项目编号
        QueryWrapper<THazardWorkPlan> wrapper = new QueryWrapper<>();
        wrapper.likeRight("code", PROJECT_PREFIX + currentDate) // 匹配指定日期的项目编号
                .orderByDesc("code") // 按项目编号降序排列
                .last("LIMIT 1"); // 只查询一条记录

        THazardWorkPlan maxProject = this.getOne(wrapper);
        // 生成新的项目编号
        String newProjectCode;
        if (maxProject != null) {
            // 提取当前序号并自增
            String maxCode = maxProject.getCode();
            int currentSerial = Integer.parseInt(maxCode.substring(maxCode.length() - 3)) + 1;
            newProjectCode = PROJECT_PREFIX + currentDate + String.format("%03d", currentSerial);
        } else {
            // 如果没有找到最大编号，从001开始
            newProjectCode = PROJECT_PREFIX + currentDate + "001";
        }

        return newProjectCode;
    }

    @Override
    public String getOrgNamesByOrgId(String orgId) {
        if (StringUtils.isNotBlank(orgId)) {
            String[] split = orgId.split("-");
            //如果是0是企业内部机构
            if ("0".equals(split[0])) {
                NewSysOrg sysOrg = newSysOrgService.getById(split[1]);
                if (sysOrg != null) {
                    return sysOrg.getOrgName();
                }
            } else {
                TContractorInfo tContractorInfo = tContractorInfoService.getById(split[1]);
                if (tContractorInfo != null) {
                    return tContractorInfo.getName();
                }
            }
        }
        return null;
    }


    @Override
    public String getOrgNamesByOrgIds(String orgId) {
        try{
            String orgName = "";
            if (StringUtils.isNotBlank(orgId)) {
                String[] split = orgId.split("-");
                //如果是0是企业内部机构
                if ("0".equals(split[0])) {
                    NewSysOrg sysOrg = newSysOrgService.getById(split[1]);
                    if (sysOrg != null) {
                        orgName = sysOrg.getOrgName();
                        NewSysOrg sysOrg2 = newSysOrgService.getById(sysOrg.getParentId());
                        if(sysOrg2 != null){
                            orgName = sysOrg2.getOrgName()+"/"+orgName;
                        }
                        return orgName;
                    }
                } else {
                    TContractorInfo tContractorInfo = tContractorInfoService.getById(split[1]);
                    if (tContractorInfo != null) {
                        return tContractorInfo.getName();
                    }
                }
            }
        }catch (Exception e){
            log.info("获取单位名称异常",e);
        }

        return null;
    }

    /*@Override
    public String validateOperation(String workType, String workLevel, String scheduledStartTime, String scheduledEndTime) {
        *//*中粮/北良规则如下
        动火作业高度危险: 8小时内
        动火作业较大危险: 12小时内
        动火作业一般危险: 72小时内
        高处作业高度危险: 12小时内
        高处作业较大危险: 24小时内
        高处作业一般危险: 7天内
        有限空间高度/较大/一般危险: 8小时内
        临时用电高度/较大/一般危险: 8小时内
        熏蒸作业高度: 7天内
        进出仓高度危险作业: 12小时内
        进出仓较大危险作业: 24小时内
        进出仓一般危险作业: 72小时内*//*
        SysDictData workTypeDictData = sysDictDataService.getDictDataById(workType);
        SysDictData workLevelDictData = sysDictDataService.getDictDataById(workLevel);
        String result = "在安全时间内";
        if(null != workTypeDictData && null != workLevelDictData){
            double hoursDifference = calculateHoursDifferenceWithZone(scheduledStartTime, scheduledEndTime);
            result = DangerousOperationValidator.validateOperation(
                    workTypeDictData.getDictValue(),
                    workLevelDictData.getDictValue(),
                    hoursDifference
            );
        }
        return result;
    }*/


    // 添加时区支持
    public static double calculateHoursDifferenceWithZone(String startTime, String endTime) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
                .withZone(ZoneId.of("Asia/Shanghai"));

        ZonedDateTime start = ZonedDateTime.parse(startTime, formatter);
        ZonedDateTime end = ZonedDateTime.parse(endTime, formatter);

        Duration duration = Duration.between(start, end);
        return duration.toMinutes() / 60.0;
    }

    /**
     * 提取 0-id,1-id ，格式中的id字段 返回如 id1,id2
     *
     * @param input 需要转化的id
     * @return
     */
    public String getSubstringAfterDash(String input) {
        String[] split = input.split(",");
        List<String> ids = new ArrayList<>();
        // 查找"-"的位置
        for (String id : split) {
            int index = id.indexOf("-");
            // 如果找到"-"，则截取"-"后的部分
            if (index != -1) {
                ids.add(id.substring(index + 1));  // 截取"-"后的部分
            }
        }

        // 如果没有"-"，返回原字符串
        return StringUtils.join(ids, ",");
    }


    /**
     * 根据用户id集合获取用户名集合
     *
     * @param allUserIds
     * @return
     */
    /*@Override
    public String getUserNamesByUserIds(String allUserIds) {
        List<String> userNames = new ArrayList<>();
        if (StringUtils.isNotBlank(allUserIds)) {
            String[] userIds = allUserIds.split(",");
            for (String userId : userIds) {
                userNames.add(getUserNamesByUserId(userId));
            }
        }
        return StringUtils.join(userNames, ",");
    }*/
    @Override
    public String getUserNamesByUserIds(String allUserIds) {

        if (StringUtils.isBlank(allUserIds)) {
            return null;
        }

        String[] userIds = allUserIds.split(",");

        // 1️⃣ 限流保护（防止异常数据）
        if (userIds.length > 50) {
            log.warn("userIds 数量异常：{}，已截断", userIds.length);
        }

        List<String> userNames = new ArrayList<>();
        Set<String> processed = new HashSet<>(); // 防重复

        for (String userId : userIds) {

            if (StringUtils.isBlank(userId)) {
                continue;
            }

            // 2️⃣ 防止同一个 userId 被反复查
            if (!processed.add(userId)) {
                continue;
            }

            String name = getUserNamesByUserId(userId);

            // 3️⃣ 严格过滤 null，防止上层反复触发
            if (StringUtils.isNotBlank(name)) {
                userNames.add(name);
            }
        }

        return userNames.isEmpty() ? null : String.join(",", userNames);
    }


    @Override
    public String getOrgNamesByUserIds(String allUserIds) {
        List<String> orgNames = new ArrayList<>();
        if (StringUtils.isBlank(allUserIds)) {
            return "";
        }
        String[] userIds = allUserIds.split(",");
        // 用于单位id去重
        Set<String> ids = new HashSet<>();
        for (String userId : userIds) {
            String[] split = userId.split("-");
            //如果是企业内部
            if ("0".equals(split[0])) {
                SysUser sysUser = sysUserService.getById(split[1]);
                if (sysUser == null || !ids.add(sysUser.getOrgId())) {
                    continue;
                }
                String orgId = sysUser.getOrgId();
                NewSysOrg sysOrg = newSysOrgService.getById(orgId);
                if (sysOrg != null) {
                    orgNames.add(sysOrg.getOrgName());
                }
            } else {
                TContractorPerson tContractorPerson = tContractorPersonService.getById(split[1]);
                if (tContractorPerson == null || !ids.add(tContractorPerson.getInfoId())) {
                    continue;
                }
                TContractorInfo tContractorInfo = tContractorInfoService.getById(tContractorPerson.getInfoId());
                if (tContractorInfo != null) {
                    orgNames.add(tContractorInfo.getName());
                }
            }
        }
        return String.join(",", orgNames);
    }

    @Override
    public List<HisTasksResponseDTO> getHistoryByInstanceId(HisTasksRequest hisTasksRequest, String planId) {
        if (StringUtils.isNotBlank(planId)) {
            THazardWorkPlan byId = this.getById(planId);
            if (byId != null) {
                hisTasksRequest.setProcessInstanceId(byId.getProcessId());
            }
        }
        List<HisTasksResponse> hisTasksResponses = new ArrayList<>(wfTaskController.getHistoryByInstanceId(hisTasksRequest).getData().getData());
        List<Object> processIds = tHazardConditionConfirmationRecordService.listObjs(new LambdaQueryWrapper<THazardConditionConfirmationRecord>().select(THazardConditionConfirmationRecord::getProcessId).isNotNull(THazardConditionConfirmationRecord::getProcessId).eq(THazardConditionConfirmationRecord::getPlanId, planId));

        if (processIds != null && processIds.size() > 0) {
            for (Object processId : processIds) {
                HisTasksRequest hisTasksRequest1 = new HisTasksRequest();
                hisTasksRequest1.setProcessInstanceId(processId.toString());
                List<HisTasksResponse> data = wfTaskController.getHistoryByInstanceId(hisTasksRequest1).getData().getData();
                List<HisTasksResponse> filteredData = data.stream()
                        .filter(task -> !"startEvent".equals(task.getTaskName()))
                        .collect(Collectors.toList());
                hisTasksResponses.addAll(filteredData);
            }
        }

        List<HisTasksResponseDTO> hisTasksResponseDTOS = BeanConverUtil.converList(hisTasksResponses, HisTasksResponseDTO.class);
        int disclosureCount = 0;
        int analysisCount = 0;
        int confirmCount = 0;
        for (HisTasksResponseDTO hisTasksRespons : hisTasksResponseDTOS) {
            String taskName = hisTasksRespons.getTaskName();
            Map<String, Object> vars = hisTasksRespons.getVars();
            //把相同名称的节点且不是驳回vars复制给新的(只获取不是驳回节点的参数)
            if (taskName != null && taskName.contains("安全许可证") && !taskName.contains("安全许可证申请")) {
                if (vars == null) {
                    List<HisTasksResponse> collect = hisTasksResponses.stream()
                            .filter(task -> task != null && StringUtils.isNotBlank(task.getTaskName()) && task.getTaskName().equals(taskName))  // 先过滤任务名不相同的任务
                            .filter(task -> {
                                Map<String, Object> map = task.getVars();
                                if (map == null) {
                                    return false;  // vars 为 null，跳过
                                }
                                Object isOnSiteConfirmation = map.get("isOnSiteConfirmation");
                                if (isOnSiteConfirmation == null || !"0".equals(isOnSiteConfirmation.toString())) {
                                    return false;  // 不是“现场确认”
                                }
                                Object rejected = map.get("rejected");
                                return rejected != null && "0".equals(rejected.toString());  // 不是“驳回”
                            }).sorted(Comparator.comparing(HisTasksResponse::getStartTime).reversed()).collect(Collectors.toList());
                    if (CollectionUtils.isNotEmpty(collect)) {
                        HisTasksResponse hisTasksResponse = collect.get(0);
                        Map<String, Object> vars1 = hisTasksResponse.getVars();
                        hisTasksRespons.setVars(vars1);
                    }
                }
            }
            //获取批次
            if (taskName != null && taskName.contains("安全技术交底")) {
                ++disclosureCount;
                hisTasksRespons.setBatch(disclosureCount);
            }
            if (taskName != null && taskName.contains("风险分析")) {
                ++analysisCount;
            }
            if (taskName != null && taskName.contains("安全条件确认")) {
                ++confirmCount;
                //已经过技术交底，才会产生第二次的条件确认
                if (disclosureCount == 0) {
                    //没过技术交底都是第一批
                    hisTasksRespons.setBatch(1);
                } else {
                    Integer batchConfirmCount = confirmCount - analysisCount + 1;
                    hisTasksRespons.setBatch(batchConfirmCount);
                }
            }

        }

        hisTasksResponseDTOS.sort(Comparator.comparing(
                task -> task.getCompleteTime() != null ? task.getCompleteTime() : task.getStartTime(),
                Comparator.nullsFirst(Comparator.naturalOrder())
        ));

        return hisTasksResponseDTOS;
    }

    @Override
    public IPage<TodoTaskDTO> selectToDoList(TodoListRequest todoListRequest) {
        Long page = StringHelper.isEmpty(todoListRequest.getPage()) ? BizConstants.PAGE : Long.valueOf(todoListRequest.getPage());
        Long limit = StringHelper.isEmpty(todoListRequest.getLimit()) ? 10L : Long.valueOf(todoListRequest.getLimit());
        Page<TodoTask> resultPage = new Page(page, limit);
        TodoTaskSqlParam todoTaskSqlParam = new TodoTaskSqlParam();
        String loginUserId = ContextUtils.getLoginUserId();
        if (StringHelper.isNotBlank(todoListRequest.getUsername())) {
            todoTaskSqlParam.setUserId(todoListRequest.getUsername());
        } else {
            todoTaskSqlParam.setUserId(loginUserId);
        }

        if (StringHelper.isNotBlank(todoListRequest.getStartUser())) {
            todoTaskSqlParam.setUserName(todoListRequest.getStartUser());
        }

        if (StringHelper.isNotBlank(todoListRequest.getProcessDefinitionName())) {
            todoTaskSqlParam.setProDefName(todoListRequest.getProcessDefinitionName());
        }

        if (StringHelper.isNotBlank(todoListRequest.getProcessDefinitionId())) {
            todoTaskSqlParam.setProDefId(todoListRequest.getProcessDefinitionId());
        }

        if (StringHelper.isNotBlank(todoListRequest.getProcessDefinitionKey())) {
            todoTaskSqlParam.setProDefKey(todoListRequest.getProcessDefinitionKey());
        }

        if (StringHelper.isNotBlank(todoListRequest.getOrgId())) {
            todoTaskSqlParam.setOrgId(todoListRequest.getOrgId());
        } else {
            UserInfo curUser = ContextUtils.getLoginUser();
            todoTaskSqlParam.setOrgId(curUser.getOrgId());
        }

        if (StringHelper.isNotBlank(todoListRequest.getProcessDefinitionKey())) {
            todoTaskSqlParam.setProDefKeys(Arrays.asList(todoListRequest.getProcessDefinitionKey().split(",")));
        }

        IPage<TodoTask> queryList = tHazardWorkPlanDao.selectToDoList(resultPage, todoTaskSqlParam);
        List<TodoTask> resultList = queryList.getRecords();
        List<TodoTaskDTO> todoTaskDTOS = BeanConverUtil.converList(resultList, TodoTaskDTO.class);
        todoTaskDTOS.forEach((task) -> {

            task.setAssigned(StringHelper.isBlank(task.getAssignee()) ? "0" : "1");
            task.setProcessInstanceSuspended(((ProcessInstance) this.runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult()).isSuspended());
            THazardWorkPlanDTO detailsById = this.getDetailsById(task.getBizId());

            String processDefinitionId = task.getProcessDefinitionId();
            //如果是跨天待办
            if (processDefinitionId.contains("hazardousWorkDailyCheck")){
                HisTasksRequest hisTasksRequest = new HisTasksRequest();
                hisTasksRequest.setProcessInstanceId(task.getProcessInstanceId());
                List<HisTasksResponseDTO> collect = getHistoryByInstanceId(hisTasksRequest, task.getBizId()).stream().filter(hisTask -> StringUtils.isNotBlank(hisTask.getTaskId()) && hisTask.getTaskId().equals(task.getTaskId())).collect(Collectors.toList());
                if (collect.size() > 0) {
                    Integer batch = collect.get(0).getBatch();
                    detailsById.setBatch(batch);
                }

            }

            task.setData(detailsById);
        });
        IPage<TodoTaskDTO> todoTaskDTOIPage = new Page<>();
        todoTaskDTOIPage.setTotal(queryList.getTotal());
        todoTaskDTOIPage.setCurrent(queryList.getCurrent());
        todoTaskDTOIPage.setSize(queryList.getSize());
        todoTaskDTOIPage.setPages(queryList.getPages());
        todoTaskDTOIPage.setRecords(todoTaskDTOS);

        return todoTaskDTOIPage;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rejectedProcess(THazardWorkPlanDTO tHazardWorkPlanDTO) {
        String planId = tHazardWorkPlanDTO.getId();
        CompleteTask completeTask = new CompleteTask();
        completeTask.setBizId(planId);
        completeTask.setTaskId(tHazardWorkPlanDTO.getTaskId());
        Map<String, Object> vars = new HashMap<>(tHazardWorkPlanDTO.getMap());
        THazardWorkPlan tHazardWorkPlan = this.getById(planId);
        String operationUnit = tHazardWorkPlan.getOperationUnit();
        String workSupervisoryUnit = tHazardWorkPlan.getWorkSupervisoryUnit();
        vars.put("level", "99");
        //如果作业所在单位和作业主管单位一致
        if (operationUnit.equals(workSupervisoryUnit)) {
            vars.put("sameOrganization", "1");
        } else {
            vars.put("sameOrganization", "0");
        }
        //不走现场确认
        vars.put("isOnSiteConfirmation", "99");
        vars.put("rejected", "1");
        getCheckUser(tHazardWorkPlan, vars);
        completeTask.setVars(vars);
        // 添加日志验证
        log.info("设置的流程变量 - checkUser: {}, workSiteResponsible: {}",
                vars.get("checkUser"), vars.get("workSiteResponsible"));
        wfTaskController.completeTask(completeTask).getData().getResult();
        //清除已经选过的人员
        String licenseCheckUser = removeLastPart(tHazardWorkPlan.getLicenseCheckUser());
        THazardWorkPlan updWorkPlan = new THazardWorkPlan();
        updWorkPlan.setId(planId);
        updWorkPlan.setLicenseCheckUser(licenseCheckUser);
        this.updateById(updWorkPlan);
    }

    public String removeLastPart(String str) {
        if (str == null) {
            return "";
        }
        // 去掉末尾的逗号
        if (str.endsWith(",")) {
            str = str.substring(0, str.length() - 1);
        }
        String[] parts = str.split(",");
        if (parts.length > 1) {
            String[] newParts = new String[parts.length - 1];
            System.arraycopy(parts, 0, newParts, 0, parts.length - 1);
            return String.join(",", newParts);
        } else {
            return "";
        }
    }


    void getCheckUser(THazardWorkPlan tHazardWorkPlan, Map<String, Object> map) {

        String licenseCheckUsers = tHazardWorkPlan.getLicenseCheckUser();


        if (StringUtils.isBlank(licenseCheckUsers)) {
            map.remove("checkUser");
            map.put("workSiteResponsible", tHazardWorkPlan.getWorkSiteResponsible());
            return;
        }
        String[] licenseCheckUserArray = licenseCheckUsers.split(",");
        int length = licenseCheckUserArray.length;
        if (length == 0) {
            map.remove("checkUser");
            map.put("workSiteResponsible", tHazardWorkPlan.getWorkSiteResponsible());
        } else {
            //获取最后一用户
            String userId = licenseCheckUserArray[length - 1];
            map.put("checkUser", userId);
            map.remove("workSiteResponsible");
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelProcess(CompleteTask completeTask) {
        THazardWorkPlan byId = this.getById(completeTask.getBizId());
        Map<String, Object> vars = completeTask.getVars();
        String opinion = "作业中止";
        if (StringHelper.isNotEmpty(vars) && StringHelper.isNotNull(vars.get("opinion"))) {
            opinion = (String) vars.get("opinion");
        }
        //关闭主流程数据
        try {
            //runtimeService.deleteProcessInstance(byId.getProcessId(), opinion);
            if(byId.getProcessId() != null){
                ruTaskService.stopProcess(byId.getProcessId());
            }
        }catch (FlowableObjectNotFoundException e) {
            log.error(e.getMessage());
        }
        cancelChildTasks(completeTask);
        THazardWorkPlan tHazardWorkPlan = new THazardWorkPlan();
        tHazardWorkPlan.setId(completeTask.getBizId());
        tHazardWorkPlan.setWorkStatus(WorkPlanStatusEnum.CANCELED.getValue());
        tHazardWorkPlan.setCancelOpinion(opinion);
        this.updateById(tHazardWorkPlan);
    }

    @Override
    public void cancelChildTasks(CompleteTask completeTask) {
        String bizId = completeTask.getBizId();
        //关闭子流程数据
        List<THazardConditionConfirmationRecord> tHazardConditionConfirmationRecords = tHazardConditionConfirmationRecordService.list(
                new QueryWrapper<THazardConditionConfirmationRecord>().eq(THazardConditionConfirmationRecord.PLAN_ID, bizId));
        for (THazardConditionConfirmationRecord tHazardConditionConfirmationRecord : tHazardConditionConfirmationRecords) {
            ruTaskService.stopProcess(tHazardConditionConfirmationRecord.getProcessId());
            //runtimeService.deleteProcessInstance(, "取消作业");
            tHazardConditionConfirmationRecord.setRemarks("取消作业");
            tHazardConditionConfirmationRecordService.updateById(tHazardConditionConfirmationRecord);
        }
    }


    // 获取前两个任务
    private HisTasksResponse getPreviousTask(List<HisTasksResponse> sortedByOriginalStartTime, int latestTaskIndex) {
        if (latestTaskIndex > 1) {
            return sortedByOriginalStartTime.get(latestTaskIndex - 2);
        }
        return sortedByOriginalStartTime.get(0);
    }

    // 从变量中获取指定的任务ID
    private String getTaskVariable(Map<String, Object> vars, String key) {
        return Optional.ofNullable(vars.get(key))
                .map(Object::toString)
                .orElse("");
    }


    @Override
    public THazardWorkPlan updateEntity(THazardWorkPlan updateRequest) {
        String escalatedLevel = getEscalatedLevel(updateRequest.getWorkLevel(), updateRequest.getIsEscalated());
        updateRequest.setEscalatedLevel(escalatedLevel);
        this.updateById(updateRequest);
        return updateRequest;
    }


    @Override
    public IPage<HisTaskDTO> hisTaskList(HisTaskListRequest hisTaskListRequest) {
        Long page = StringHelper.isEmpty(hisTaskListRequest.getPage()) ? BizConstants.PAGE : Long.valueOf(hisTaskListRequest.getPage());
        Long limit = StringHelper.isEmpty(hisTaskListRequest.getLimit()) ? BizConstants.LIMIT : Long.valueOf(hisTaskListRequest.getLimit());
        Page<HisTask> resultPage = new Page(page, limit);
        HisTaskSqlParam hisTaskSqlParam = new HisTaskSqlParam();
        String loginUserId = ContextUtils.getLoginUserId();
        if (StringHelper.isNotBlank(hisTaskListRequest.getUsername())) {
            hisTaskSqlParam.setUserId(hisTaskListRequest.getUsername());
        } else {
            hisTaskSqlParam.setUserId(loginUserId);
        }

        if (StringHelper.isNotBlank(hisTaskListRequest.getStartUser())) {
            hisTaskSqlParam.setUserName(hisTaskListRequest.getStartUser());
        }

        if (StringHelper.isNotBlank(hisTaskListRequest.getProcessDefinitionName())) {
            hisTaskSqlParam.setProDefName(hisTaskListRequest.getProcessDefinitionName());
        }
        IPage<HisTask> queryList = this.actRuTaskService.selectHisTaskList(resultPage, hisTaskSqlParam);
        List<HisTaskDTO> hisTaskDTOS = BeanConverUtil.converList(queryList.getRecords(), HisTaskDTO.class);
        for (HisTaskDTO hisTaskDTO : hisTaskDTOS) {
            THazardWorkPlanDTO detailsById = this.getDetailsById(hisTaskDTO.getBizId());
            hisTaskDTO.setData(detailsById);
        }
        IPage<HisTaskDTO> hisTaskDTOIPage = new Page<>();
        hisTaskDTOIPage.setTotal(queryList.getTotal());
        hisTaskDTOIPage.setCurrent(queryList.getCurrent());
        hisTaskDTOIPage.setSize(queryList.getSize());
        hisTaskDTOIPage.setPages(queryList.getPages());
        hisTaskDTOIPage.setRecords(hisTaskDTOS);
        return hisTaskDTOIPage;
    }

    @SneakyThrows
    @Override
    public boolean exportJobTicket(HttpServletResponse response, THazardWorkPlanParam param) {
        // 时间格式化工具
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //附录序号
        Integer index = 1;
        List<XWPFDocument> wordList = new ArrayList<>();

        // 获取工作计划详细信息
        THazardWorkPlanDTO planDTO = this.getDetailsById(param.getId());

        // 填充风险分析的模板
        index = buildRiskAnalysis(wordList, planDTO, sdf, index);

        //条件确认模板
        index = buildConditionConfirmation(wordList, planDTO, sdf, index);

        //许可证模板
        index = buildLicense(wordList, planDTO, sdf, index);

        buildDisclosure(wordList, planDTO, sdf, index);

        if (StringHelper.isEmpty(wordList)) {
            return false;
        }
        // 设置响应头
        response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
        response.setHeader("Content-Disposition", "attachment; filename=workPermit.docx");
        //合并模板
        XWPFDocument xwpfDocument = mergeWord(wordList);
        response.getOutputStream();
        xwpfDocument.write(response.getOutputStream());

        // 合并 Word 文档
        XWPFDocument mergedDocument = mergeWord(wordList);

        // 设置响应头
        response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
        response.setHeader("Content-Disposition", "attachment; filename=workPermit.docx");

        // 将合并后的 Word 文档写入响应流
        try (OutputStream out = response.getOutputStream()) {
            mergedDocument.write(out);
        } catch (IOException e) {
           log.error("作业票下载异常:",e);
            return false;
        } finally {
            // 关闭合并后的文档资源
            if (mergedDocument != null) {
                try {
                    mergedDocument.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    log.error("作业票下载异常:闭合并后的文档资源",e);
                }
            }
        }

        return true;
    }

    @Override
    public List<TaskInfoDTO> getAllTaskApprovers(String processInstanceId, String planId) {
        List<TaskInfoDTO> allTaskApprovers = ruTaskService.getAllTaskApprovers(processInstanceId);
        List<Object> processIds = tHazardConditionConfirmationRecordService.listObjs(new LambdaQueryWrapper<THazardConditionConfirmationRecord>().select(THazardConditionConfirmationRecord::getProcessId).isNotNull(THazardConditionConfirmationRecord::getProcessId).eq(THazardConditionConfirmationRecord::getPlanId, planId));
        if (processIds != null && processIds.size() > 0) {
            for (Object processId : processIds) {
                List<TaskInfoDTO> data = ruTaskService.getAllTaskApprovers(processId.toString());
                List<TaskInfoDTO> filteredData = data.stream()
                        .filter(task -> !"startEvent".equals(task.getTaskName()))
                        .collect(Collectors.toList());
                allTaskApprovers.addAll(filteredData);
            }
        }
        List<TaskInfoDTO> filteredData = allTaskApprovers.stream()
                .filter(task -> !"基本信息".equals(task.getTaskName())).sorted(Comparator.comparing(
                        task -> task.getEndTime() != null ? task.getEndTime() : task.getStartTime(),
                        Comparator.nullsFirst(Comparator.naturalOrder())
                )).collect(Collectors.toList());
        return filteredData;
    }

    @SneakyThrows
    private void buildDisclosure(List<XWPFDocument> wordList, THazardWorkPlanDTO planDTO, SimpleDateFormat sdf,
                                 Integer index) {
        Map<String, Object> datas = new HashMap<>();
        //计划信息
        populatePlanDetails(planDTO, sdf, datas);

        // 被交底单位/部门
        /*String operator = planDTO.getOperator();
        String orgNamesByUserIds = this.getOrgNamesByUserIds(operator);
        datas.put("disclosedUnitName", orgNamesByUserIds);*/

        // 被交底单位/部门  取作业所在单位
        datas.put("disclosedUnitName", getOrgNamesByOrgIds(planDTO.getOperationUnit()));

        List<THazardDisclosure> disclosureList = tHazardDisclosureService.list(new QueryWrapper<THazardDisclosure>().eq(THazardDisclosure.PLAN_ID, planDTO.getId()));
        // 按照批次分组
        Map<Integer, List<THazardDisclosure>> listMap = disclosureList.stream().collect(Collectors.groupingBy(THazardDisclosure::getBatch));
        for (Map.Entry<Integer, List<THazardDisclosure>> entry : listMap.entrySet()) {
            Integer batch = entry.getKey();
            String format = String.format("第%s次", Convert.numberToChinese(batch, false));
            /*for (THazardDisclosure tHazardDisclosure : disclosureList) {
                if (tHazardDisclosure != null) {
                    populateHazardDisclosureDetails(tHazardDisclosure, sdf, datas, format);
                    datas.put("index", index);
                    index++;
                }
                Resource resource = resourceLoader.getResource("classpath:word/hazardousWorkPermit/disclosure.docx");
                InputStream inputStream = resource.getInputStream();
                //InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("word/hazardousWorkPermit/disclosure.docx");
                if (inputStream == null) {
                    return;
                }
                XWPFDocument document = new MyXWPFDocument(inputStream);

                // 导出Word
                WordExportUtil.exportWord07(document, datas);
                wordList.add(document);
            }*/
            for (THazardDisclosure tHazardDisclosure : entry.getValue()) {
                if (tHazardDisclosure != null) {
                    populateHazardDisclosureDetails(tHazardDisclosure, sdf, datas, format);
                    datas.put("index", index);
                    index++;
                }
                // 加载模板（可优化到循环外）
                Resource resource = resourceLoader.getResource("classpath:word/hazardousWorkPermit/disclosure.docx");
                InputStream inputStream = resource.getInputStream();
                if (inputStream == null) {
                    return;
                }

                XWPFDocument document = new MyXWPFDocument(inputStream);
                WordExportUtil.exportWord07(document, datas);
                wordList.add(document);
            }
        }

    }

    private void populateHazardDisclosureDetails(THazardDisclosure tHazardDisclosure, SimpleDateFormat sdf,
                                                 Map<String, Object> datas, String batch) {
        List<THazardDisclosureDetails> tHazardDisclosureDetailsList =
                tHazardDisclosureDetailsService.list(new QueryWrapper<THazardDisclosureDetails>().eq(THazardDisclosureDetails.DISCLOSURE_ID, tHazardDisclosure.getId()).orderByAsc(THazardDisclosureDetails.SORT));
        for (THazardDisclosureDetails details : tHazardDisclosureDetailsList) {
            //details.setIsDisclosure(ExcelUtil.convertByExp(details.getIsDisclosure(), "1=交底,2=不涉及", ","));
            details.setIsDisclosure(ExcelUtil.convertByExp(details.getIsDisclosure(), "1=交底,0=不涉及", ","));
        }
        datas.put("tHazardDisclosureDetails", tHazardDisclosureDetailsList);

        // 存放签字信息
        List<JobTicketSignatureInfo> disclosureSignatureInfos = new ArrayList<>();

        // 交底人签字信息
        addSignatureInfo(disclosureSignatureInfos, "交底人签字", tHazardDisclosure.getDisclosurePersonSign(), sdf);

        // 监护人签字信息
        addSignatureInfo(disclosureSignatureInfos, "监护人签字", tHazardDisclosure.getGuardianSignature(), sdf);

        // 作业人签字
        addSignatureInfo(disclosureSignatureInfos, "作业人签字", tHazardDisclosure.getRecipientSignature(), sdf);
        if (StringUtils.isNotEmpty(disclosureSignatureInfos)) {
            datas.put("disclosureSignatureInfos", disclosureSignatureInfos);
        }

        String workType = sysDictDataService.getDictDetailedValue(tHazardDisclosure.getWorkTypeId(), "/");
        workType = workType.replace("不涉及/", "").replace("涉及/", "");

        datas.put("title", batch + workType);
        datas.put("disclosureStartTime", tHazardDisclosure.getDisclosureStartTime());
        datas.put("disclosureEndTime", tHazardDisclosure.getDisclosureEndTime());

    }

    // 填充许可证模板
    @SneakyThrows
    private Integer buildLicense(List<XWPFDocument> wordList, THazardWorkPlanDTO planDTO, SimpleDateFormat sdf,
                                 Integer index) {
        Map<String, Object> datas = new HashMap<>();
        //计划信息
        populatePlanDetails(planDTO, sdf, datas);
        //获取许可证
        THazardLicense tHazardLicense = tHazardLicenseService.getOne(new QueryWrapper<THazardLicense>().eq(THazardLicense.PLAN_ID, planDTO.getId()));
        if (tHazardLicense != null) {
            populateCustomField(planDTO, tHazardLicense, datas);
            // 风险分析相关数据
            populateHazardLicenseDetails(tHazardLicense, sdf, datas, index);
            datas.put("index", index);
            String workType = sysDictDataService.getDictDetailedValue(planDTO.getWorkType(), "/");
            workType = workType.replace("不涉及/", "").replace("涉及/", "");
            datas.put("workType", workType);
            Resource resource = resourceLoader.getResource("classpath:word/hazardousWorkPermit/license.docx");
            InputStream inputStream = resource.getInputStream();
           // InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("word/hazardousWorkPermit/license.docx");
            if (inputStream == null) {
                return index;
            }
            XWPFDocument document = new MyXWPFDocument(inputStream);
            // 导出Word
            WordExportUtil.exportWord07(document, datas);
            wordList.add(document);
        }
        index++;
        return index;
    }

    private Integer populateHazardLicenseDetails(THazardLicense tHazardLicense, SimpleDateFormat sdf, Map<String, Object> datas, Integer index) {
        List<THazardLicenseAnalysisInfo> tHazardLicenseAnalysisInfos = tHazardLicenseAnalysisInfoService.list(new QueryWrapper<THazardLicenseAnalysisInfo>().eq(THazardLicenseAnalysisInfo.LICENSE_ID, tHazardLicense.getId()));

        List<Map<String, Object>> analysisInfoMapList = new ArrayList<>();
        for (THazardLicenseAnalysisInfo tHazardLicenseAnalysisInfo : tHazardLicenseAnalysisInfos) {
            Map<String, Object> analysisInfoMap = new HashMap<>();
            analysisInfoMap.put("analysisInfo", tHazardLicenseAnalysisInfo);
            JobTicketSignatureInfo signatureInfo = new JobTicketSignatureInfo();
            ImageEntity signatureImage = buildImg(tHazardLicenseAnalysisInfo.getTesterSignature());
            signatureInfo.setSignatureImage(signatureImage);
            analysisInfoMap.put("signatureInfo", signatureInfo);
            String measurementTime = tHazardLicenseAnalysisInfo.getMeasurementTime() == null ? "" :
                    DateUtil.convertDateToStr(tHazardLicenseAnalysisInfo.getMeasurementTime(), "yyyy-MM-dd HH:mm:ss");
//                    tHazardLicenseAnalysisInfo.getMeasurementTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            analysisInfoMap.put("timeLocation", StringUtils.format("{}{}",
                    measurementTime, tHazardLicenseAnalysisInfo.getMeasurementLocation()));
            analysisInfoMapList.add(analysisInfoMap);
        }
        datas.put("tHazardLicenseAnalysisInfos", analysisInfoMapList);

        // 存放签字信息
        List<JobTicketSignatureInfo> licenseAnalysisinfoSignatureInfos = new ArrayList<>();
        // 现场负责人签字信息
        addSignatureInfo(licenseAnalysisinfoSignatureInfos, "现场负责人签字", tHazardLicense.getWorkSiteResponsibleSignature(), sdf);

        // 监护人签字信息
        addSignatureInfo(licenseAnalysisinfoSignatureInfos, "监护人签字", tHazardLicense.getGuardianSignature(),
                sdf);
        List<THazardLicenseSignatureRecord> hazardLicenseSignatureRecords =
                tHazardLicenseSignatureRecordService.list(new QueryWrapper<THazardLicenseSignatureRecord>().eq(THazardLicenseSignatureRecord.LICENSE_ID, tHazardLicense.getId()).orderByAsc(THazardLicenseSignatureRecord.UPDATE_DATE));
        // 许可证审核签字信息
        for (THazardLicenseSignatureRecord record : hazardLicenseSignatureRecords) {
            LicenseSignatureTypeEnum typeEnum = LicenseSignatureTypeEnum.getByCode(record.getType());
            if (typeEnum == null && StringHelper.isEmpty(record.getTypeName())) {
                continue;
            }
            String signatureName = typeEnum != null ? typeEnum.getDesc() : record.getTypeName();
            addSignatureInfo(licenseAnalysisinfoSignatureInfos, signatureName, record.getSignature(), sdf);
        }
        datas.put("licenseAnalysisinfoSignatureInfos", licenseAnalysisinfoSignatureInfos);

        return null;
    }

    /***
     * 填充工作计划详情
     * @param planDTO
     * @param datas
     */
    void populateCustomField(THazardWorkPlanDTO planDTO, THazardLicense tHazardLicense, Map<String, Object> datas) {

        List<CustomField> customFields = new ArrayList<>();
        String workType = planDTO.getWorkType();
        SysDictData workTypeDict = sysDictDataService.getDictTypeDetail(workType, null, null);
        String dictKey = workTypeDict.getDictKey();
        if (dictKey.equals(WorkTypeEnum.ENTRY_WAREHOUSE.getValue()) || dictKey.equals(WorkTypeEnum.EXIT_WAREHOUSE.getValue()) || dictKey.equals(WorkTypeEnum.CLOSE_POSITION.getValue())) {
            customFields.add(new CustomField("作业类型", tHazardLicense.getWorkType()));
            customFields.add(new CustomField("特定作业", tHazardLicense.getSpecificTask()));
        } else if (dictKey.equals(WorkTypeEnum.HEIGHT_WORK.getValue())) {
            customFields.add(new CustomField("高处作业设备工具", tHazardLicense.getElevatedEquipmentName()));
            customFields.add(new CustomField("悬挂位置", tHazardLicense.getSuspensionLocation()));
            customFields.add(new CustomField("作业类型", tHazardLicense.getWorkType()));
            customFields.add(new CustomField("特定作业", tHazardLicense.getSpecificTask()));
            customFields.add(new CustomField("涉及其他的危险作业", tHazardLicense.getInvolvesOtherHazardousWork()));
        } else if (dictKey.equals(WorkTypeEnum.HOT_WORK.getValue())) {
            customFields.add(new CustomField("动火类型", tHazardLicense.getWorkType()));
            customFields.add(new CustomField("涉及其他的危险作业", tHazardLicense.getInvolvesOtherHazardousWork()));
            customFields.add(new CustomField("特定作业", tHazardLicense.getSpecificTask()));
            customFields.add(new CustomField("作业风险", tHazardLicense.getSpecificTask()));
        } else if (dictKey.equals(WorkTypeEnum.CONFINED_SPACE_WORK.getValue())) {
            customFields.add(new CustomField("设施所属单位", tHazardLicense.getFacilityUnit()));
            customFields.add(new CustomField("设施名称", tHazardLicense.getFacilityName()));
            customFields.add(new CustomField("空间原有介质", tHazardLicense.getOriginalMedium()));
            customFields.add(new CustomField("主要危险因素", tHazardLicense.getPrimaryHazardFactors()));
            customFields.add(new CustomField("涉及其他的危险作业", tHazardLicense.getInvolvesOtherHazardousWork()));
            customFields.add(new CustomField("特定作业", tHazardLicense.getSpecificTask()));
            customFields.add(new CustomField("作业风险", tHazardLicense.getSpecificTask()));
        }

        datas.put("customFields", customFields);
    }




    // 填充风险分析的模板
    @SneakyThrows
    /*private Integer buildConditionConfirmation(List<XWPFDocument> wordList, THazardWorkPlanDTO planDTO, SimpleDateFormat sdf, Integer index) {
        Map<String, Object> datas = new HashMap<>();
        populatePlanDetails(planDTO, sdf, datas);

        // 获取条件确认
        List<THazardConditionConfirmation> tHazardConditionConfirmations = tHazardConditionConfirmationService.list(new QueryWrapper<THazardConditionConfirmation>()
                .eq(THazardConditionConfirmation.PLAN_ID, planDTO.getId()));
        //按批次分组
        Map<Integer, List<THazardConditionConfirmation>> listMap = tHazardConditionConfirmations.stream().collect(Collectors.groupingBy(THazardConditionConfirmation::getBatch));
        for (Map.Entry<Integer, List<THazardConditionConfirmation>> entry : listMap.entrySet()) {
            //批次
            Integer batch = entry.getKey();
            String format = String.format("第%s次", Convert.numberToChinese(batch, false));
            for (THazardConditionConfirmation tHazardConditionConfirmation : tHazardConditionConfirmations) {
                if (tHazardConditionConfirmation != null) {
                    // 风险分析相关数据
                    populateHazardConditionConfirmationDetails(tHazardConditionConfirmation, sdf, datas, format);
                    datas.put("index", index);
                    index++;
                }
                Resource resource = resourceLoader.getResource("classpath:word/hazardousWorkPermit/conditionConfirmation.docx");
                InputStream inputStream = resource.getInputStream();
                // InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("word/hazardousWorkPermit/conditionConfirmation.docx");
                XWPFDocument document = new MyXWPFDocument(inputStream);
                // 导出Word
                WordExportUtil.exportWord07(document, datas);
                wordList.add(document);
            }
        }
        return index;
    }*/
    private Integer buildConditionConfirmation(List<XWPFDocument> wordList, THazardWorkPlanDTO planDTO, SimpleDateFormat sdf, Integer index) {
        // 获取基础数据（不变的部分）
        Map<String, Object> baseDatas = new HashMap<>();
        populatePlanDetails(planDTO, sdf, baseDatas);

        // 获取条件确认
        List<THazardConditionConfirmation> tHazardConditionConfirmations = tHazardConditionConfirmationService.list(
                new QueryWrapper<THazardConditionConfirmation>()
                        .eq(THazardConditionConfirmation.PLAN_ID, planDTO.getId()));

        if (tHazardConditionConfirmations == null || tHazardConditionConfirmations.isEmpty()) {
            return index;
        }

        // 按批次分组
        Map<Integer, List<THazardConditionConfirmation>> listMap = tHazardConditionConfirmations.stream()
                .collect(Collectors.groupingBy(THazardConditionConfirmation::getBatch));

        // 预加载模板到字节数组，避免多次IO
        byte[] templateBytes;
        try (InputStream templateStream = resourceLoader.getResource("classpath:word/hazardousWorkPermit/conditionConfirmation.docx").getInputStream()) {
            templateBytes = IOUtils.toByteArray(templateStream);
        } catch (IOException e) {
            log.error("加载Word模板失败", e);
            return index;
        }

        for (Map.Entry<Integer, List<THazardConditionConfirmation>> entry : listMap.entrySet()) {
            Integer batch = entry.getKey();
            String format = String.format("第%s次", Convert.numberToChinese(batch, false));

            // 循环当前批次的数据
            for (THazardConditionConfirmation tHazardConditionConfirmation : entry.getValue()) {
                if (tHazardConditionConfirmation == null) {
                    continue;
                }

                try {
                    // 为每个文档创建独立的数据Map
                    Map<String, Object> datas = new HashMap<>(baseDatas);

                    // 填充当前记录的特有数据
                    populateHazardConditionConfirmationDetails(tHazardConditionConfirmation, sdf, datas, format);
                    datas.put("index", index);
                    index++;

                    // 从字节数组创建文档，避免文件IO
                    try (ByteArrayInputStream inputStream = new ByteArrayInputStream(templateBytes)) {
                        XWPFDocument document = new MyXWPFDocument(inputStream);
                        WordExportUtil.exportWord07(document, datas);
                        wordList.add(document);
                    }
                } catch (Exception e) {
                    log.error("生成Word文档失败，记录ID: {}", tHazardConditionConfirmation.getId(), e);
                    // 继续处理下一条记录，不中断整个流程
                }
            }
        }
        return index;
    }

    private void populateHazardConditionConfirmationDetails(THazardConditionConfirmation tHazardConditionConfirmation, SimpleDateFormat sdf, Map<String, Object> datas, String batch) {
        List<THazardConditionConfirmationDetails> tHazardConditionConfirmationDetails =
                tHazardConditionConfirmationDetailsService.list(new QueryWrapper<THazardConditionConfirmationDetails>().eq(THazardConditionConfirmationDetails.CONDITION_CONFIRMATION_ID, tHazardConditionConfirmation.getId()).orderByAsc("sort"));
        tHazardConditionConfirmationDetails.forEach(tHazardConditionConfirmationDetail -> {
            tHazardConditionConfirmationDetail.setResult(ExcelUtil.convertByExp(tHazardConditionConfirmationDetail.getResult(), "0=否,1=是,2=不涉及", ","));
        });

        datas.put("conditionConfirmationDetails", tHazardConditionConfirmationDetails);
        // 存放签字信息
        List<JobTicketSignatureInfo> conditionConfirmationSignatureInfos = new ArrayList<>();

        addSignatureInfo(conditionConfirmationSignatureInfos, "作业现场负责人或安全员签字",
                tHazardConditionConfirmation.getWorkSiteResponsibleSignature(), sdf);
        addSignatureInfo(conditionConfirmationSignatureInfos, "安全管理部门签字", tHazardConditionConfirmation.getDepartmentSignature(), sdf);

        String workType = sysDictDataService.getDictDetailedValue(tHazardConditionConfirmation.getWorkTypeId(), "/");
        workType = workType.replace("不涉及/", "").replace("涉及/", "");

        datas.put("title", batch + workType);
        datas.put("conditionConfirmationSignatureInfos", conditionConfirmationSignatureInfos);
        datas.put("confirmStartTime", DateUtil.convertDateToStr(tHazardConditionConfirmation.getCreateDate(), "yyyy-MM-dd HH:mm:ss"));
        datas.put("confirmEndTime", DateUtil.convertDateToStr(tHazardConditionConfirmation.getUpdateDate(), "yyyy-MM-dd HH:mm:ss"));
    }


    // 填充风险分析的模板
    @SneakyThrows
    private Integer buildRiskAnalysis(List<XWPFDocument> wordList, THazardWorkPlanDTO planDTO, SimpleDateFormat sdf, Integer index) {
        Map<String, Object> datas = new HashMap<>();
        populatePlanDetails(planDTO, sdf, datas);

        // 获取风险评估信息
        List<THazardAssessment> tHazardAssessments = tHazardAssessmentService.list(new QueryWrapper<THazardAssessment>()
                .eq(THazardAssessment.PLAN_ID, planDTO.getId()));

        for (THazardAssessment tHazardAssessment : tHazardAssessments) {
            if (tHazardAssessment != null) {
                // 风险分析相关数据
                populateHazardAssessmentDetails(tHazardAssessment, sdf, datas);
            }
            Resource resource = resourceLoader.getResource("classpath:word/hazardousWorkPermit/riskAnalysis.docx");
            InputStream inputStream = resource.getInputStream();
            //InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("word/hazardousWorkPermit/riskAnalysis.docx");
            if (inputStream == null) {
                return index;
            }
            XWPFDocument document = new MyXWPFDocument(inputStream);
            datas.put("index", index);
            // 导出Word
            WordExportUtil.exportWord07(document, datas);
            wordList.add(document);
            index++;
        }
        return index;
    }


    // 提取工作计划数据填充方法
    private void populatePlanDetails(THazardWorkPlanDTO planDTO, SimpleDateFormat sdf, Map<String, Object> datas) {
        datas.put("workUnit", planDTO.getWorkUnitName());
        datas.put("workContent", planDTO.getWorkContent());
        datas.put("scheduledStartTime", sdf.format(planDTO.getScheduledStartTime()));
        datas.put("scheduledEndTime", sdf.format(planDTO.getScheduledEndTime()));
        datas.put("workLocation", planDTO.getWorkLocation());
        datas.put("workSiteResponsibleName", planDTO.getWorkSiteResponsibleName());
        datas.put("guardianName", planDTO.getGuardianName());
        datas.put("operatorName", planDTO.getOperatorName());
        datas.put("supervisorName", planDTO.getSupervisorName());
        datas.put("discloserName", planDTO.getDiscloserName());
        String workTime = StringUtils.format("{} 至 {}", sdf.format(planDTO.getScheduledStartTime()),
                sdf.format(planDTO.getScheduledEndTime()));
        if (planDTO.getActualStartTime() != null && planDTO.getActualEndTime() != null) {
            workTime = StringUtils.format("{}(实际作业时间：{} 至 {})", workTime, sdf.format(planDTO.getActualStartTime()),
                    sdf.format(planDTO.getActualEndTime()));
            datas.put("actualStartTime", sdf.format(planDTO.getActualStartTime()));
            datas.put("actualEndTime", sdf.format(planDTO.getActualEndTime()));
        }
        datas.put("workTime", workTime);

        SysDictData workLevel = sysDictDataService.getDictTypeDetail(planDTO.getWorkLevel(), null, null);
        if (workLevel != null) {
            datas.put("workLevel", workLevel);
        }
        datas.put("code", planDTO.getCode());
    }

    // 提取风险评估相关数据填充方法
    private void populateHazardAssessmentDetails(THazardAssessment tHazardAssessment, SimpleDateFormat sdf, Map<String, Object> datas) {
        List<THazardAssessmentDetails> tHazardAssessmentDetails = tHazardAssessmentDetailsService
                .list(new QueryWrapper<THazardAssessmentDetails>().eq(THazardAssessmentDetails.ASSESSMENT_ID, tHazardAssessment.getId()).orderByAsc(THazardAssessmentDetails.SORT));
        for (THazardAssessmentDetails tHazardAssessmentDetail : tHazardAssessmentDetails) {
            tHazardAssessmentDetail.setIsSatisfiedName(ExcelUtil.convertByExp(tHazardAssessmentDetail.getIsSatisfied(), "0=否,1=是,2=不涉及", ","));
        }

        datas.put("tHazardAssessmentDetails", tHazardAssessmentDetails);

        // 存放签字信息
        List<JobTicketSignatureInfo> assessmentDetailSignatureInfos = new ArrayList<>();

        // 现场负责人签字信息
        addSignatureInfo(assessmentDetailSignatureInfos, "现场负责人签字", tHazardAssessment.getWorkSiteResponsibleSignature(), sdf);

        // 参与人签字信息
        addSignatureInfo(assessmentDetailSignatureInfos, "参与人签字", tHazardAssessment.getParticipantSignature(), sdf);

        if (!assessmentDetailSignatureInfos.isEmpty()) {
            datas.put("assessmentDetailSignatureInfos", assessmentDetailSignatureInfos);
        }

        //作业类型
        String workType = sysDictDataService.getDictDetailedValue(tHazardAssessment.getWorkTypeId(), "/");
        if (StringUtils.isNotBlank(workType)) {
            workType = workType.replace("不涉及/", "").replace("涉及/", "");
            datas.put("workType", workType);
        }
    }

    // 提取签字信息填充方法
    private void addSignatureInfo(List<JobTicketSignatureInfo> signatureInfos, String signatureName, String signaturePath, SimpleDateFormat sdf) {
        if (signaturePath != null && !signaturePath.isEmpty()) {
            String[] signaturePaths = signaturePath.split(",");
            for (String path : signaturePaths) {
                if (StringUtils.isBlank(path)) {
                    continue;
                }
                JobTicketSignatureInfo signatureInfo = new JobTicketSignatureInfo();
                ImageEntity signatureImage = buildImg(path);
                signatureInfo.setSignatureName(signatureName);
                signatureInfo.setSignatureImage(signatureImage);
                String fileName = FileUtil.getFileNameWithoutExtension(path);
                // 使用文件名中的时间戳来生成签字时间
                try {
                    String signatureTime = sdf.format(Long.parseLong(Objects.requireNonNull(fileName)));
                    signatureInfo.setSignatureTime(signatureTime);
                } catch (Exception ex) {
                    log.error("生成签字时间异常，签字图片地址：" + path, ex);
                }
                signatureInfos.add(signatureInfo);
            }
        }
    }


    public ImageEntity buildImg(String realImagePath) {
        ImageEntity simage = new ImageEntity();
        simage.setHeight(50);
        simage.setWidth(50);
        //将图片的本地路径导入进去
        simage.setUrl(realImagePath);
        //将simage对象的类型设置为URL类型。
        //该图像实体表示的是一个通过URL链接获取的远程图像，而不是一个本地存储的图像。
        simage.setType(ImageEntity.URL);
        return simage;
    }


    /**
     * word文件合并
     *
     * @param wordList
     * @return
     * @throws Exception
     */
    public static XWPFDocument mergeWord(List<XWPFDocument> wordList) throws Exception {
        if (CollectionUtils.isEmpty(wordList)) {
            throw new RuntimeException("待合并的word文档list为空");
        }
        XWPFDocument doc = wordList.get(0);
        int size = wordList.size();
        if (size > 1) {
            doc.createParagraph().setPageBreak(true);
            for (int i = 1; i < size; i++) {
                // 从第二个word开始合并
                XWPFDocument nextPageDoc = wordList.get(i);
                // 最后一页不需要设置分页符
                if (i != (size - 1)) {
                    nextPageDoc.createParagraph().setPageBreak(true);
                }
                appendBody(doc, nextPageDoc);
            }
        }
        return doc;
    }

    private static void appendBody(XWPFDocument src, XWPFDocument append) throws Exception {
        CTBody src1Body = src.getDocument().getBody();
        CTBody src2Body = append.getDocument().getBody();
        List<XWPFPictureData> allPictures = append.getAllPictures();
        // 记录图片合并前及合并后的ID
        Map<String, String> map = new HashMap<>();
        for (XWPFPictureData picture : allPictures) {
            String before = append.getRelationId(picture);
            //将原文档中的图片加入到目标文档中
            String after = src.addPictureData(picture.getData(), Document.PICTURE_TYPE_PNG);
            map.put(before, after);
        }
        appendBody(src1Body, src2Body, map);
    }

    private static void appendBody(CTBody src, CTBody append, Map<String, String> map) throws Exception {
        XmlOptions optionsOuter = new XmlOptions();
        optionsOuter.setSaveOuter();
        String appendString = append.xmlText(optionsOuter);
        String srcString = src.xmlText();
        String prefix = srcString.substring(0, srcString.indexOf(">") + 1);
        String mainPart = srcString.substring(srcString.indexOf(">") + 1, srcString.lastIndexOf("<"));
        String sufix = srcString.substring(srcString.lastIndexOf("<"));
        String addPart = appendString.substring(appendString.indexOf(">") + 1, appendString.lastIndexOf("<"));
        if (map != null && !map.isEmpty()) {
            //对xml字符串中图片ID进行替换
            for (Map.Entry<String, String> set : map.entrySet()) {
                addPart = addPart.replace(set.getKey(), set.getValue());
            }
        }
        //将两个文档的xml内容进行拼接
        CTBody makeBody = CTBody.Factory.parse(prefix + mainPart + addPart + sufix);
        src.set(makeBody);
    }


    /**
     * @param workLevelId
     * @return
     */
    public String getEscalatedLevel(String workLevelId, String isEscalated) {
        //处理提及后状态
        SysDictData workLevelDict = sysDictDataService.getDictTypeDetail(workLevelId, null, null);
        if (workLevelDict != null) {
            int workLevel = Integer.parseInt(workLevelDict.getDictKey());
            isEscalated = StringUtils.isNotBlank(isEscalated) ? isEscalated : "0";
            int escalatedLevel = workLevel + Integer.parseInt(isEscalated);
            if (escalatedLevel > 3) {
                escalatedLevel = 3;
            }
            SysDictData dictTypeDetail = sysDictDataService.getDictTypeDetail(null, "dange_work_level", String.valueOf(escalatedLevel));
            if (dictTypeDetail != null) {
                return dictTypeDetail.getDictDataId();
            }
        }
        return null;
    }

    /**
     * 根据用户id获取用户名
     *
     * @param userId
     * @return
     */
    /*public String getUserNamesByUserId(String userId) {
        if (StringUtils.isNotBlank(userId)) {
            String[] split = userId.split("-");
            //0为企业内部人员
            if ("0".equals(split[0])) {
                SysUser sysUser = sysUserService.getById(split[1]);
                if (sysUser != null) {
                    return sysUser.getUserName();
                }
            } else if ("1".equals(split[0])) {
                TContractorPerson tContractorPerson = tContractorPersonService.getById(split[1]);
                if (tContractorPerson != null) {
                    return tContractorPerson.getName();
                }
            } else {
                log.info("split-------->"+split);
                if(split.length>1){
                    return split[1];
                }
                return null;
            }
        }
        return null;
    }*/

    public String getUserNamesByUserId(String userId) {

        // 1. 快速失败（避免无效请求反复进入）
        if (StringUtils.isBlank(userId)) {
            return null;
        }

        // 2. userId 必须包含 -
        if (!userId.contains("-")) {
            log.warn("非法 userId 格式：{}", userId);
            return userId; // 直接返回原值，避免反复计算
        }

        String[] split = userId.split("-", 2); // 最多分 2 段

        if (split.length < 2 || StringUtils.isBlank(split[1])) {
            log.warn("userId 分割异常：{}", userId);
            return userId;
        }

        String type = split[0];
        String id = split[1];

        try {
            switch (type) {
                case "0":
                    SysUser sysUser = sysUserService.getById(id);
                    return sysUser != null ? sysUser.getUserName() : null;

                case "1":
                    TContractorPerson person = tContractorPersonService.getById(id);
                    return person != null ? person.getName() : null;

                default:
                    log.warn("未知 userId 类型：{}", userId);
                    return id;
            }
        } catch (Exception e) {
            // 3. 兜底防止异常导致上层重试
            log.error("getUserNamesByUserId 异常，userId={}", userId, e);
            return null;
        }
    }



    @SneakyThrows
    @Override
    @XxlJob("hazardousWorkMessageAlerts")
    public void hazardousWorkMessageAlerts() {
        //获取所有未完成的工作计划
        QueryWrapper<THazardWorkPlan> query = new QueryWrapper<>();
        query.le(THazardWorkPlan.ACTUAL_START_TIME, new Date());
        query.isNotNull(THazardWorkPlan.ACTUAL_START_TIME);
        query.gt(THazardWorkPlan.ACTUAL_END_TIME, new Date());
        List<THazardWorkPlan> tHazardWorkPlans = this.list(query);
        // 当前时间
        Date currentTime = new Date();
        for (THazardWorkPlan tHazardWorkPlan : tHazardWorkPlans) {
            Date endTime = tHazardWorkPlan.getActualEndTime();  // 获取工作计划的结束时间
            if (endTime != null) {
                // 计算提前提醒时间和作业超时时间
                Date reminderTime = new Date(endTime.getTime() - tHazardWorkPlan.getEarlyReminderTime() * 60 * 1000); // 提前提醒时间单位：分钟

                // 如果是当天发送提醒
                if (DateUtils.isSameDay(reminderTime, currentTime) && currentTime.before(reminderTime)) {
                    // 安排提前提醒任务
                    schedulePreReminder(tHazardWorkPlan);
                    // 安排超时提醒任务
                    //scheduleOverReminder(tHazardWorkPlan);
                }

                // 安排超时提醒任务
                scheduleOverReminder(tHazardWorkPlan);
            }
        }
    }

    @SneakyThrows
    private void schedulePreReminder(THazardWorkPlan tHazardWorkPlan) {
        Date reminderTime = new Date(tHazardWorkPlan.getActualEndTime().getTime() - tHazardWorkPlan.getEarlyReminderTime() * 60 * 1000);
        String jobIdentity = "ReminderJob-pre-" + tHazardWorkPlan.getId();
        scheduler.deleteJob(new JobKey(jobIdentity, "ReminderGroup"));
        JobDetail jobDetail = JobBuilder.newJob(ReminderJob.class)
                .withIdentity(jobIdentity, "ReminderGroup")
                .usingJobData("id", tHazardWorkPlan.getId())
                .usingJobData("remindType", RemindConstants.REMIND_TYPE_PRE)
                .build();

        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(jobIdentity, "ReminderGroup")
                .startAt(reminderTime)
                .build();

        scheduler.scheduleJob(jobDetail, trigger);
    }

    @SneakyThrows
    private void scheduleOverReminder(THazardWorkPlan tHazardWorkPlan) {
        Date endTime = tHazardWorkPlan.getActualEndTime();
        String jobIdentity = "ReminderJob-over-" + tHazardWorkPlan.getId();
        scheduler.deleteJob(new JobKey(jobIdentity, "ReminderGroup"));
        JobDetail jobDetail = JobBuilder.newJob(ReminderJob.class)
                .withIdentity(jobIdentity, "ReminderGroup")
                .usingJobData("id", tHazardWorkPlan.getId())
                .usingJobData("remindType", RemindConstants.REMIND_TYPE_OVER)
                .build();

        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(jobIdentity, "ReminderGroup")
                .startAt(endTime)
                .build();

        scheduler.scheduleJob(jobDetail, trigger);
    }
    /* Private Methods */

    /**
     * 列表查询条件及查询参数
     */
    private QueryWrapper<THazardWorkPlan> createQuery(THazardWorkPlanParam queryParam) {
        QueryWrapper<THazardWorkPlan> queryWrapper = new QueryWrapper<>();
        if (StringHelper.isNotEmpty(queryParam.getCode())) {
            queryWrapper.like("code", queryParam.getCode());
        }
        if (StringHelper.isNotEmpty(queryParam.getWorkType())) {
            queryWrapper.eq(THazardWorkPlan.WORK_TYPE, queryParam.getWorkType());
        }
        if (StringHelper.isNotEmpty(queryParam.getWorkLevel())) {
            queryWrapper.eq(THazardWorkPlan.WORK_LEVEL, queryParam.getWorkLevel());
        }
        if (StringHelper.isNotEmpty(queryParam.getStakeholderWork())) {
            queryWrapper.eq(THazardWorkPlan.STAKEHOLDER_WORK, queryParam.getStakeholderWork());
        }
        if (StringHelper.isNotEmpty(queryParam.getInvolvesOtherHazardousWork())) {
            queryWrapper.eq(THazardWorkPlan.INVOLVES_OTHER_HAZARDOUS_WORK, queryParam.getInvolvesOtherHazardousWork());
        }
        if (StringHelper.isNotEmpty(queryParam.getIsHoliday())) {
            queryWrapper.eq(THazardWorkPlan.IS_HOLIDAY, queryParam.getIsHoliday());
        }
        if (StringHelper.isNotEmpty(queryParam.getIsEscalated())) {
            queryWrapper.eq(THazardWorkPlan.IS_ESCALATED, queryParam.getIsEscalated());
        }
        if (StringHelper.isNotEmpty(queryParam.getEscalatedLevel())) {
            queryWrapper.eq(THazardWorkPlan.ESCALATED_LEVEL, queryParam.getEscalatedLevel());
        }
        if (ObjectHelper.isNotEmpty(queryParam.getScheduledStartTime())) {
            queryWrapper.ge(THazardWorkPlan.SCHEDULED_START_TIME, queryParam.getScheduledStartTime());
        }
        if (ObjectHelper.isNotEmpty(queryParam.getHazardWorkPlanIds())) {
            queryWrapper.in(THazardWorkPlan.ID, queryParam.getHazardWorkPlanIds());
        }
        if (ObjectHelper.isNotEmpty(queryParam.getScheduledEndTime())) {
            queryWrapper.le(THazardWorkPlan.SCHEDULED_END_TIME, queryParam.getScheduledEndTime());
        }
        if (ObjectHelper.isNotEmpty(queryParam.getActualStartTime())) {
            queryWrapper.ge(THazardWorkPlan.ACTUAL_START_TIME, queryParam.getScheduledStartTime());
        }
        if (ObjectHelper.isNotEmpty(queryParam.getActualEndTime())) {
            queryWrapper.le(THazardWorkPlan.ACTUAL_END_TIME, queryParam.getScheduledEndTime());
        }
        if (StringHelper.isNotEmpty(queryParam.getWorkContent())) {
            queryWrapper.like(THazardWorkPlan.WORK_CONTENT, queryParam.getWorkContent());
        }
        if (StringHelper.isNotEmpty(queryParam.getWorkLocation())) {
            queryWrapper.like(THazardWorkPlan.WORK_LOCATION, queryParam.getWorkLocation());
        }
        if (StringHelper.isNotEmpty(queryParam.getWorkSupervisoryUnit())) {
            queryWrapper.eq(THazardWorkPlan.WORK_SUPERVISORY_UNIT, queryParam.getWorkSupervisoryUnit());
        }
        if (StringHelper.isNotEmpty(queryParam.getWorkSiteResponsible())) {
            queryWrapper.like(THazardWorkPlan.WORK_SITE_RESPONSIBLE, queryParam.getWorkSiteResponsible());
        }
        if (StringHelper.isNotEmpty(queryParam.getWorkSiteResponsibleSignature())) {
            queryWrapper.eq(THazardWorkPlan.WORK_SITE_RESPONSIBLE_SIGNATURE, queryParam.getWorkSiteResponsibleSignature());
        }
        if (StringHelper.isNotEmpty(queryParam.getProjectLeader())) {
            queryWrapper.eq(THazardWorkPlan.PROJECT_LEADER, queryParam.getProjectLeader());
        }
        if (StringHelper.isNotEmpty(queryParam.getGuardian())) {
            queryWrapper.eq(THazardWorkPlan.GUARDIAN, queryParam.getGuardian());
        }
        if (StringHelper.isNotEmpty(queryParam.getSupervisor())) {
            queryWrapper.eq(THazardWorkPlan.SUPERVISOR, queryParam.getSupervisor());
        }
        if (StringHelper.isNotEmpty(queryParam.getOperator())) {
            queryWrapper.eq(THazardWorkPlan.OPERATOR, queryParam.getOperator());
        }
        if (StringHelper.isNotEmpty(queryParam.getRiskAnalysisParticipant())) {
            queryWrapper.eq(THazardWorkPlan.RISK_ANALYSIS_PARTICIPANT, queryParam.getRiskAnalysisParticipant());
        }
        if (StringHelper.isNotEmpty(queryParam.getDiscloser())) {
            queryWrapper.eq(THazardWorkPlan.DISCLOSER, queryParam.getDiscloser());
        }
        if (ObjectHelper.isNotEmpty(queryParam.getEarlyReminderTime())) {
            queryWrapper.eq(THazardWorkPlan.EARLY_REMINDER_TIME, queryParam.getEarlyReminderTime());
        }
        if (StringHelper.isNotEmpty(queryParam.getWorkPlanFile())) {
            queryWrapper.eq(THazardWorkPlan.WORK_PLAN_FILE, queryParam.getWorkPlanFile());
        }
        if (StringHelper.isNotEmpty(queryParam.getWorkSiteResponsibleEndSignature())) {
            queryWrapper.eq(THazardWorkPlan.WORK_SITE_RESPONSIBLE_END_SIGNATURE, queryParam.getWorkSiteResponsibleEndSignature());
        }
        if (StringHelper.isNotEmpty(queryParam.getWorkStatus())) {
            if (queryParam.getWorkStatus().equals(WorkPlanStatusEnum.UNDER_APPROVAL.getValue())) {
                queryWrapper.eq(THazardWorkPlan.WORK_STATUS, WorkPlanStatusEnum.PERMIT.getValue()).isNotNull("license_status");
            } else if (queryParam.getWorkStatus().equals(WorkPlanStatusEnum.IN_PROGRESS.getValue())) {
                queryWrapper.eq(THazardWorkPlan.WORK_STATUS, WorkPlanStatusEnum.TO_BE_CLOSED.getValue());
            } else {
                queryWrapper.eq(THazardWorkPlan.WORK_STATUS, queryParam.getWorkStatus());
            }
        }
        if (StringHelper.isNotEmpty(queryParam.getOrgId())) {
//            queryWrapper.eq(THazardWorkPlan.ORG_ID,queryParam.getOrgId());
            /*List<String> idsChildrenById = newSysOrgService.getIdsChildrenById(queryParam.getOrgId());

            idsChildrenById.add(queryParam.getOrgId());
            //查询下级机构id
            queryWrapper.in(THazardWorkPlan.ORG_ID, idsChildrenById);*/

            List<String> idsChildrenById = new  ArrayList<>();

            if(queryParam.isFlag()){
                List<String> list = Arrays.stream(queryParam.getOrgId().split(","))
                        .filter(s -> !s.trim().isEmpty()) // 忽略空值
                        .collect(Collectors.toList());
                for(String orgId:list){
                    idsChildrenById.addAll(newSysOrgService.getIdsChildrenById(orgId));
                }
                idsChildrenById.addAll(list);
            }else{
                List<String> list = Arrays.stream(queryParam.getOrgId().split(","))
                        .filter(s -> !s.trim().isEmpty()) // 忽略空值
                        .collect(Collectors.toList());
                boolean isDbgk = false;
                for(String orgId:list){
                    NewSysOrg org = newSysOrgService.getOne(new QueryWrapper<NewSysOrg>().eq("org_id", orgId));
                    if(org.getParentIds().contains(com.testor.common.core.constant.Constants.DBGK_ORG_ID)){
                        isDbgk = true;
                    }
                    idsChildrenById.addAll(newSysOrgService.getIdsChildrenById(orgId));
                }
                idsChildrenById.addAll(list);
                //如果是东北港口下的部门,查询东北港口所有
                if(isDbgk){
                    idsChildrenById.addAll(newSysOrgService.dbgkChildOrgIds());
                    idsChildrenById.add(com.testor.common.core.constant.Constants.DBGK_ORG_ID);
                }
            }

            //查询下级机构id
            queryWrapper.in(THazardWorkPlan.ORG_ID, idsChildrenById);

        }
        if (StringHelper.isNotEmpty(queryParam.getStatus())) {
            queryWrapper.eq(THazardWorkPlan.STATUS, queryParam.getStatus());
        } else {
            queryWrapper.ne(THazardWorkPlan.STATUS, BizConstants.STATUS_DELETE);
        }
        if (StringHelper.isNotEmpty(queryParam.getStatOrgId())) {
            List<String> idsChildrenById = newSysOrgService.getIdsChildrenById(queryParam.getStatOrgId());
            idsChildrenById.add(queryParam.getOrgId());
            //查询下级机构id
            queryWrapper.in(THazardWorkPlan.ORG_ID, idsChildrenById);
        }
        if (StringHelper.isNotEmpty(queryParam.getReportStartDate())) {
            queryWrapper.ge(THazardWorkPlan.CREATE_DATE, queryParam.getReportStartDate());
        }
        if (StringHelper.isNotEmpty(queryParam.getReportEndDate())) {
            queryWrapper.le(THazardWorkPlan.CREATE_DATE, queryParam.getReportEndDate());
        }
        if (StringHelper.isNotEmpty(queryParam.getNeWorkStatus())) {
            queryWrapper.ne(THazardWorkPlan.WORK_STATUS, queryParam.getNeWorkStatus());
        }
        if (StringHelper.isNotEmpty(queryParam.getQueryContent())) {
            queryWrapper.like(THazardWorkPlan.WORK_CONTENT, queryParam.getQueryContent());
            queryWrapper.or().like(THazardWorkPlan.WORK_LOCATION, queryParam.getQueryContent());
        }
        if (StringHelper.isNotEmpty(queryParam.getOrderBy())) {
            if (StringHelper.isNotEmpty(queryParam.getOrderType())
                    && BizConstants.ASC.equals(queryParam.getOrderType())) {
                queryWrapper.orderByAsc(queryParam.getOrderBy());
            } else {
                queryWrapper.orderByDesc(queryParam.getOrderBy());
            }
        } else {
            queryWrapper.orderByDesc(THazardWorkPlan.SCHEDULED_START_TIME);
        }
        return queryWrapper;
    }


    @Override
    public void transferTask(TransferDTO request) {
        try {
            String taskId = request.getTaskId();
            String newAssignee = request.getNewAssignee();
            String reason = request.getReason();
            String currentUserId = request.getCurrentUserId();

            // 验证任务是否存在
            Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
            if (task == null) {
                log.info("任务不存在或已完成");
                throw new com.testor.common.core.exception.ServiceException("任务不存在或已完成");
            }

            if(task.getAssignee() != null){
                // 权限验证：只有当前负责人可以转签
                if (!task.getAssignee().equals(currentUserId) ) {
                    log.info("无权限转签此任务");
                    throw new com.testor.common.core.exception.ServiceException("无权限转签此任务");
                }
            }


            // 验证新负责人
            SysUser newUser = sysUserService.getById(newAssignee);
            if (newUser == null) {
                log.info("新负责人不存在");
                throw new com.testor.common.core.exception.ServiceException("新负责人不存在");
            }

            // 记录原负责人
            String originalAssignee = task.getAssignee();

            // 执行转签
            taskService.setAssignee(taskId, newAssignee);

            // 添加转签注释
            taskService.addComment(taskId, task.getProcessInstanceId(),
                    "任务转签: " + originalAssignee + " → " + newAssignee + ", 原因: " + reason);

            log.info("任务转签成功: {}", taskId);
        } catch (Exception e) {
            log.error("转签任务失败: {}", e.getMessage(), e);
            throw new com.testor.common.core.exception.ServiceException("转签任务失败: " + e.getMessage());
        }
    }

    /**
     * 撤回流程到上一个节点（或指定节点）
     * @param request
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void recallProcess(CompleteTask completeTask) {
        THazardWorkPlan byId = this.getById(completeTask.getBizId());
        Map<String, Object> vars = completeTask.getVars();
        String opinion = "作业撤回";
        if (StringHelper.isNotEmpty(vars) && StringHelper.isNotNull(vars.get("opinion"))) {
            opinion = (String) vars.get("opinion");
        }

        // 先删除已办记录
        deleteDoneRecords(completeTask.getBizId());


        //关闭主流程数据
        try {
            if(byId.getProcessId() != null){
                ruTaskService.stopProcess(byId.getProcessId());
            }
        }catch (FlowableObjectNotFoundException e) {
            log.error(e.getMessage());
        }
        cancelChildTasks(completeTask);
        THazardWorkPlan tHazardWorkPlan = new THazardWorkPlan();
        tHazardWorkPlan.setId(completeTask.getBizId());
        tHazardWorkPlan.setWorkStatus(WorkPlanStatusEnum.DRAFT.getValue());
        tHazardWorkPlan.setRevokeOpinion(opinion);
        this.updateById(tHazardWorkPlan);
    }

    @Override
    public Page<THazardWorkPlanRiverside> riversideList(THazardWorkPlanParam param) {
        Long page =
                StringHelper.isEmpty(param.getPage()) ? BizConstants.PAGE : Long.valueOf(param.getPage());
        Long limit =
                StringHelper.isEmpty(param.getLimit()) ? BizConstants.LIMIT : Long.valueOf(param.getLimit());
        Page<THazardWorkPlan> resultPage = new Page<>(page, limit);
        return baseMapper.riversideList(resultPage, param);
    }

    @Override
    public Integer completedCount(List<String> orgIds) {
        return baseMapper.completedCount(orgIds);
    }

    @Override
    public Integer unfinishedCount(List<String> orgIds) {
        return baseMapper.unfinishedCount(orgIds);
    }



    private void deleteDoneRecords(String bizId) {
        try {
            // 获取流程实例ID
            THazardWorkPlan entity = this.getById(bizId);
            if (entity != null && entity.getProcessId() != null) {
                String processInstanceId = entity.getProcessId();

                // 删除历史任务记录（已办记录）
                List<HistoricTaskInstance> historicTasks = historyService.createHistoricTaskInstanceQuery()
                        .processInstanceId(processInstanceId)
                        .list();

                for (HistoricTaskInstance task : historicTasks) {
                    // 删除历史任务
                    historyService.deleteHistoricTaskInstance(task.getId());
                }
            }
        } catch (Exception e) {
            log.error("删除已办记录失败，业务ID: {}", bizId, e);
            // 根据业务需求决定是否抛出异常
        }
    }


}
