package com.hotwj.platform.workbench.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hotwj.platform.config.companyBasicConfig.domain.bo.HotCompanyBasicConfigBo;
import com.hotwj.platform.config.companyBasicConfig.domain.vo.HotCompanyBasicConfigVo;
import com.hotwj.platform.config.companyBasicConfig.service.IHotCompanyBasicConfigService;
import com.hotwj.platform.config.hiddenDangerCheckConfig.domain.bo.HotHiddenDangerCheckConfigBo;
import com.hotwj.platform.config.hiddenDangerCheckConfig.domain.vo.HotHiddenDangerCheckConfigVo;
import com.hotwj.platform.config.hiddenDangerCheckConfig.service.IHotHiddenDangerCheckConfigService;
import com.hotwj.platform.config.hiddenDangerInspectStore.domain.bo.HotHiddenDangerInspectStoreBo;
import com.hotwj.platform.config.hiddenDangerInspectStore.domain.vo.HotHiddenDangerInspectStoreVo;
import com.hotwj.platform.config.hiddenDangerInspectStore.service.IHotHiddenDangerInspectStoreService;
import com.hotwj.platform.config.vehicleInspectionConfig.domain.bo.HotVehicleInspectionConfigBo;
import com.hotwj.platform.config.vehicleInspectionConfig.domain.vo.HotVehicleInspectionConfigVo;
import com.hotwj.platform.config.vehicleInspectionConfig.service.IHotVehicleInspectionConfigService;
import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain.bo.HotVehicleSecondaryMaintenanceConfigBo;
import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain.vo.HotVehicleSecondaryMaintenanceConfigVo;
import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.service.IHotVehicleSecondaryMaintenanceConfigService;
import com.hotwj.platform.config.vehicleThreeInspectConfig.domain.bo.HotVehicleThreeInspectConfigBo;
import com.hotwj.platform.config.vehicleThreeInspectConfig.domain.vo.HotVehicleThreeInspectConfigVo;
import com.hotwj.platform.config.vehicleThreeInspectConfig.service.IHotVehicleThreeInspectConfigService;
import com.hotwj.platform.driverManagement.driver.domain.HotDriver;
import com.hotwj.platform.driverManagement.driver.domain.bo.HotDriverBo;
import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo;
import com.hotwj.platform.driverManagement.driver.mapper.HotDriverMapper;
import com.hotwj.platform.driverManagement.driver.service.IHotDriverService;
import com.hotwj.platform.driverManagement.driverLaborContract.domain.bo.HotDriverLaborContractBo;
import com.hotwj.platform.driverManagement.driverLaborContract.domain.vo.HotDriverLaborContractVo;
import com.hotwj.platform.driverManagement.driverLaborContract.service.IHotDriverLaborContractService;
import com.hotwj.platform.noticeManagerment.adminNotice.domain.HotAdminNotice;
import com.hotwj.platform.noticeManagerment.adminNotice.mapper.HotAdminNoticeMapper;
import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo;
import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService;
import com.hotwj.platform.resourceManagement.companyInsurance.domain.bo.HotCompanyInsuranceBo;
import com.hotwj.platform.resourceManagement.companyInsurance.domain.vo.HotCompanyInsuranceVo;
import com.hotwj.platform.resourceManagement.companyInsurance.service.IHotCompanyInsuranceService;
import com.hotwj.platform.resourceManagement.companyLaborContract.domain.bo.HotCompanyLaborContractBo;
import com.hotwj.platform.resourceManagement.companyLaborContract.domain.vo.HotCompanyLaborContractVo;
import com.hotwj.platform.resourceManagement.companyLaborContract.service.IHotCompanyLaborContractService;
import com.hotwj.platform.resourceManagement.companySafetyManager.domain.bo.HotCompanySafetyManagerBo;
import com.hotwj.platform.resourceManagement.companySafetyManager.domain.vo.HotCompanySafetyManagerVo;
import com.hotwj.platform.resourceManagement.companySafetyManager.service.IHotCompanySafetyManagerService;
import com.hotwj.platform.resourceManagement.vehicleAnnualReview.domain.vo.HotVehicleAnnualReviewVo;
import com.hotwj.platform.resourceManagement.vehicleAnnualReview.service.IHotVehicleAnnualReviewService;
import com.hotwj.platform.resourceManagement.vehicleContract.domain.bo.HotVehicleContractBo;
import com.hotwj.platform.resourceManagement.vehicleContract.domain.vo.HotVehicleContractVo;
import com.hotwj.platform.resourceManagement.vehicleContract.service.IHotVehicleContractService;
import com.hotwj.platform.resourceManagement.vehicleInsurance.domain.bo.HotVehicleInsuranceBo;
import com.hotwj.platform.resourceManagement.vehicleInsurance.domain.vo.HotVehicleInsuranceVo;
import com.hotwj.platform.resourceManagement.vehicleInsurance.service.IHotVehicleInsuranceService;
import com.hotwj.platform.resourceManagement.vehicleMaintenance.domain.vo.HotVehicleMaintenanceVo;
import com.hotwj.platform.resourceManagement.vehicleMaintenance.service.IHotVehicleMaintenanceService;
import com.hotwj.platform.resourceManagement.vehicleManagement.domain.HotVehicle;
import com.hotwj.platform.resourceManagement.vehicleManagement.domain.bo.HotVehicleBo;
import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo;
import com.hotwj.platform.resourceManagement.vehicleManagement.mapper.HotVehicleMapper;
import com.hotwj.platform.resourceManagement.vehicleManagement.service.IHotVehicleService;
import com.hotwj.platform.resourceManagement.vehicleManagement.utils.VehicleUtils;
import com.hotwj.platform.resourceManagement.vehicleTerminalInstall.domain.bo.HotVehicleTerminalInstallBo;
import com.hotwj.platform.resourceManagement.vehicleTerminalInstall.domain.vo.HotVehicleTerminalInstallVo;
import com.hotwj.platform.resourceManagement.vehicleTerminalInstall.service.IHotVehicleTerminalInstallService;
import com.hotwj.platform.securityManagement.accidentArchive.domain.HotAccidentArchive;
import com.hotwj.platform.securityManagement.accidentArchive.mapper.HotAccidentArchiveMapper;
import com.hotwj.platform.securityManagement.hiddenDanger.domain.HotHiddenDanger;
import com.hotwj.platform.securityManagement.hiddenDanger.mapper.HotHiddenDangerMapper;
import com.hotwj.platform.securityManagement.hiddenDangerInspection.domain.HotHiddenDangerInspection;
import com.hotwj.platform.securityManagement.hiddenDangerInspection.mapper.HotHiddenDangerInspectionMapper;
import com.hotwj.platform.securityManagement.hiddenDangerPlan.domain.HotHiddenDangerPlan;
import com.hotwj.platform.securityManagement.hiddenDangerPlan.mapper.HotHiddenDangerPlanMapper;
import com.hotwj.platform.securityManagement.training.domain.bo.HotTrainingBo;
import com.hotwj.platform.securityManagement.training.domain.vo.HotTrainingVo;
import com.hotwj.platform.securityManagement.training.service.IHotTrainingService;
import com.hotwj.platform.securityManagement.trainingParticipant.domain.HotTrainingParticipant;
import com.hotwj.platform.securityManagement.trainingParticipant.domain.bo.HotTrainingParticipantBo;
import com.hotwj.platform.securityManagement.trainingParticipant.domain.vo.HotTrainingParticipantVo;
import com.hotwj.platform.securityManagement.trainingParticipant.mapper.HotTrainingParticipantMapper;
import com.hotwj.platform.securityManagement.trainingParticipant.service.IHotTrainingParticipantService;
import com.hotwj.platform.securityManagement.violationInfo.domain.HotViolationInfo;
import com.hotwj.platform.securityManagement.violationInfo.mapper.HotViolationInfoMapper;
import com.hotwj.platform.workbench.domain.vo.*;
import com.hotwj.platform.workbench.mapper.HotTrainingDailyStatsMapper;
import com.hotwj.platform.workbench.service.IWorkbenchService;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 工作台服务实现
 */
@Service
@RequiredArgsConstructor
public class WorkbenchServiceImpl implements IWorkbenchService {

    private final ISysCompanyService sysCompanyService;
    private final IHotCompanySafetyManagerService hotCompanySafetyManagerService;
    private final IHotDriverService hotDriverService;
    private final IHotVehicleService hotVehicleService;
    private final IHotVehicleThreeInspectConfigService hotVehicleThreeInspectConfigService;
    private final IHotHiddenDangerInspectStoreService hotHiddenDangerInspectStoreService;
    private final IHotHiddenDangerCheckConfigService hotHiddenDangerCheckConfigService;
    private final IHotCompanyBasicConfigService hotCompanyBasicConfigService;
    private final IHotCompanyLaborContractService hotCompanyLaborContractService;
    private final IHotCompanyInsuranceService hotCompanyInsuranceService;
    private final IHotVehicleInsuranceService hotVehicleInsuranceService;
    private final IHotDriverLaborContractService hotDriverLaborContractService;
    private final IHotTrainingService hotTrainingService;
    private final IHotVehicleInspectionConfigService hotVehicleInspectionConfigService;
    private final IHotVehicleSecondaryMaintenanceConfigService hotVehicleSecondaryMaintenanceConfigService;
    private final IHotVehicleContractService hotVehicleContractService;
    private final IHotVehicleTerminalInstallService hotVehicleTerminalInstallService;
    private final IHotVehicleAnnualReviewService hotVehicleAnnualReviewService;
    private final IHotVehicleMaintenanceService hotVehicleMaintenanceService;

    @Override
    public CompanyBasicInfoVo getCompanyBasicInfo() {
        LoginUser loginUser = LoginHelper.getLoginUser();
        if (loginUser == null) {
            throw new ServiceException("未登录");
        }
        Long companyId = loginUser.getCompanyId();
        boolean superAdmin = LoginHelper.isSuperAdmin(loginUser.getUserId());
        if (companyId == null) {
            if (superAdmin) {
                return buildFallbackCompanyBasicInfo();
            }
            throw new ServiceException("企业信息不存在");
        }
        SysCompanyVo companyVo = sysCompanyService.queryById(companyId);
        if (companyVo == null) {
            if (superAdmin) {
                return buildFallbackCompanyBasicInfo();
            }
            throw new ServiceException("企业信息不存在");
        }

        CompanyBasicInfoVo result = new CompanyBasicInfoVo();
        result.setCompanyName(companyVo.getCompanyName());
        result.setResponsibleName(companyVo.getResponsibleName());

        HotCompanySafetyManagerBo managerBo = new HotCompanySafetyManagerBo();
        managerBo.setCompanyId(companyId);
        managerBo.setIsResigned(0L);
        managerBo.setStatus(1L);
        List<HotCompanySafetyManagerVo> safetyManagers = hotCompanySafetyManagerService.queryList(managerBo);

        long managerCount = safetyManagers.stream()
//                .filter(m -> (m.getIsDepartmentHead() != null && m.getIsDepartmentHead() == 1L)
//                        || (m.getCompanyHead() != null && m.getCompanyHead() == 1L))
                .count();
        result.setManagerCount(managerCount);


        Long driverCount = driverMapper.selectCount(new LambdaQueryWrapper<HotDriver>()
                .eq(HotDriver::getCompanyId, companyId)
                .eq(HotDriver::getStatus, 1L)
                .eq(HotDriver::getAuditStatus, 1)
                .eq(HotDriver::getIsDeleted, 0L));

        result.setDriverCount(driverCount);

        result.setEmployeeTotal(managerCount + driverCount);


        HotVehicleBo vehicleBo = new HotVehicleBo();
        vehicleBo.setCompanyId(companyId);
        List<HotVehicleVo> vehicles = hotVehicleService.queryList(vehicleBo);
        long vehicleCount = vehicles.stream()
                .filter(v -> v.getOperationStatus() != null && (v.getOperationStatus() == 1L || v.getOperationStatus() == 2L))
                .count();
        long normalVehicleCount = vehicles.stream()
                .filter(v -> v.getOperationStatus() != null && v.getOperationStatus() == 1L)
                .count();
        long pausedVehicleCount = vehicles.stream()
                .filter(v -> v.getOperationStatus() != null && v.getOperationStatus() == 2L)
                .count();

        result.setVehicleTotal(vehicleCount);
        result.setNormalVehicleCount(normalVehicleCount);
        result.setPausedVehicleCount(pausedVehicleCount);

        return result;
    }

    private CompanyBasicInfoVo buildFallbackCompanyBasicInfo() {
        CompanyBasicInfoVo result = new CompanyBasicInfoVo();
        result.setCompanyName("平台管理员");
        result.setResponsibleName("平台管理员");
        result.setEmployeeTotal(0L);
        result.setManagerCount(0L);
        result.setDriverCount(0L);
        result.setVehicleTotal(0L);
        result.setNormalVehicleCount(0L);
        result.setPausedVehicleCount(0L);
        return result;
    }

    private final IHotTrainingParticipantService hotTrainingParticipantService;
    private final HotAccidentArchiveMapper accidentArchiveMapper;
    private final HotViolationInfoMapper violationInfoMapper;
    private final HotHiddenDangerInspectionMapper hiddenDangerInspectionMapper;
    private final HotHiddenDangerMapper  hiddenDangerMapper;
    private final HotTrainingParticipantMapper trainingParticipantMapper;
    private final HotAdminNoticeMapper adminNoticeMapper;
    private final HotHiddenDangerPlanMapper hiddenDangerPlanMapper;
    private final HotVehicleMapper vehicleMapper;
    private final HotDriverMapper driverMapper;
    private final HotTrainingDailyStatsMapper trainingDailyStatsMapper;

    @Override
    public VehicleDriverRatioVo getVehicleDriverRatio() {
        LoginUser loginUser = LoginHelper.getLoginUser();
        if (loginUser == null) {
            throw new ServiceException("未登录");
        }
        Long companyId = loginUser.getCompanyId();

        List<Long> operationStatusList = Arrays.asList(1L, 2L);
        // 统计运营车辆数 (OperationStatus = 1)
        Long vehicleCount = vehicleMapper.selectCount(new LambdaQueryWrapper<HotVehicle>()
                .eq(HotVehicle::getCompanyId, companyId)
                .in(HotVehicle::getOperationStatus, operationStatusList)
                .eq(HotVehicle::getIsDeleted, 0L));

        // 统计驾驶员数 (Status = 1, AuditStatus = 1)
        Long driverCount = driverMapper.selectCount(new LambdaQueryWrapper<HotDriver>()
                .eq(HotDriver::getCompanyId, companyId)
                .eq(HotDriver::getStatus, 1L)
                .eq(HotDriver::getAuditStatus, 1)
                .eq(HotDriver::getIsDeleted, 0L));

        VehicleDriverRatioVo result = new VehicleDriverRatioVo();
        result.setVehicleCount(vehicleCount);
        result.setDriverCount(driverCount);

        long total = vehicleCount + driverCount;
        if (total > 0) {
            // 计算车辆占比: 车辆 / (车辆 + 驾驶员)
            BigDecimal ratio = BigDecimal.valueOf(vehicleCount)
                    .divide(BigDecimal.valueOf(total), 3, BigDecimal.ROUND_HALF_UP)
                    .multiply(BigDecimal.valueOf(100))
                    .setScale(1, BigDecimal.ROUND_HALF_UP);
            result.setVehicleRatio(ratio);
        } else {
            result.setVehicleRatio(BigDecimal.ZERO);
        }

        return result;
    }

    @Override
    public TrainingUsageVo getTrainingUsage() {
        LoginUser loginUser = LoginHelper.getLoginUser();
        if (loginUser == null) {
            throw new ServiceException("未登录");
        }
        Long companyId = loginUser.getCompanyId();

        // 查询该企业下所有未删除的培训参与记录
        List<HotTrainingParticipant> participants = trainingParticipantMapper.selectList(new LambdaQueryWrapper<HotTrainingParticipant>()
                .select(HotTrainingParticipant::getIsCompleted)
                .eq(HotTrainingParticipant::getCompanyId, companyId)
                .eq(HotTrainingParticipant::getIsDeleted, 0L));

        long total = participants.size();
        long completedCount = participants.stream()
                .filter(p -> p.getIsCompleted() != null && p.getIsCompleted() == 1L)
                .count();
        long incompleteCount = total - completedCount;

        TrainingUsageVo result = new TrainingUsageVo();
        result.setCompletedCount(completedCount);
        result.setIncompleteCount(incompleteCount);

        if (total > 0) {
            // 计算百分比，保留整数
            BigDecimal rate = BigDecimal.valueOf(completedCount)
                    .divide(BigDecimal.valueOf(total), 2, BigDecimal.ROUND_HALF_UP)
                    .multiply(BigDecimal.valueOf(100));
            result.setCompletionRate(rate);
        } else {
            result.setCompletionRate(BigDecimal.ZERO);
        }

        return result;
    }

    @Override
    public RiskReminderVo getRiskReminder() {
        LoginUser loginUser = LoginHelper.getLoginUser();
        if (loginUser == null) {
            return new RiskReminderVo();
        }
        Long companyId = loginUser.getCompanyId();
        RiskReminderVo result = new RiskReminderVo();

        // 1. 企业风险
        result.setCompanyRisk(getCompanyRisk(companyId));
        // 2. 车辆风险
        result.setVehicleRisk(getVehicleRisk(companyId));
        // 3. 驾驶员风险
        result.setDriverRisk(getDriverRisk(companyId));
        // 4. 教育培训风险
        result.setTrainingRisk(getTrainingRisk(companyId));

        return result;
    }

    @Override
    public ShortcutStatisticsVo getShortcutStatistics() {
        LoginUser loginUser = LoginHelper.getLoginUser();
        if (loginUser == null) {
            return new ShortcutStatisticsVo();
        }
        Long companyId = loginUser.getCompanyId();
        ShortcutStatisticsVo result = new ShortcutStatisticsVo();

        // 1. 事故报告：需要整改就要有消息提醒
        Long accidentCount = accidentArchiveMapper.selectCount(new LambdaQueryWrapper<HotAccidentArchive>()
                .eq(HotAccidentArchive::getCompanyId, companyId)
                .and(w -> w.eq(HotAccidentArchive::getIsRectified, 0L).or().isNull(HotAccidentArchive::getIsRectified))
                .eq(HotAccidentArchive::getIsDeleted, 0L));
        result.setAccidentReportCount(accidentCount);

        // 2. 违规信息：需要约谈可编辑的有消息提醒
        Long violationCount = violationInfoMapper.selectCount(new LambdaQueryWrapper<HotViolationInfo>()
                .eq(HotViolationInfo::getCompanyId, companyId)
                .eq(HotViolationInfo::getNeedInterview, 1L)
                .isNull(HotViolationInfo::getInterviewTime)
                .eq(HotViolationInfo::getIsDeleted, 0L));
        result.setViolationInfoCount(violationCount);

        // 3. 隐患排查：隐患排查项HotHiddenDangerInspection处理待处理或者审核中的
        // 状态: 1=待处理, 2=审核中, 4=待处理(同义), 5=审核中(同义)
        Long hiddenInspectionCount = hiddenDangerInspectionMapper.selectCount(new LambdaQueryWrapper<HotHiddenDangerInspection>()
                .eq(HotHiddenDangerInspection::getCompanyId, companyId)
                .in(HotHiddenDangerInspection::getStatus, Arrays.asList(1L, 2L, 4L, 5L))
                .eq(HotHiddenDangerInspection::getIsDeleted, 0L));
        result.setHiddenInspectionCount(hiddenInspectionCount);

        // 状态: 0=评估、1=治理、2=复查
        Long hiddenGovernanceCount = hiddenDangerMapper.selectCount(new LambdaQueryWrapper<HotHiddenDanger>()
                .eq(HotHiddenDanger::getCompanyId, companyId)
                .in(HotHiddenDanger::getStatus, Arrays.asList(0L, 1L, 2L))
                .eq(HotHiddenDanger::getIsDeleted, 0L));
        result.setHiddenGovernanceCount(hiddenGovernanceCount);

        // 4. 日常培训：审核需要消息提醒，isCompleted=1，并且isPass=null
        Long trainingCount = trainingParticipantMapper.selectCount(new LambdaQueryWrapper<HotTrainingParticipant>()
                .eq(HotTrainingParticipant::getCompanyId, companyId)
                .eq(HotTrainingParticipant::getIsCompleted, 1L)
                .isNull(HotTrainingParticipant::getIsPass)
                .eq(HotTrainingParticipant::getIsDeleted, 0L));
        result.setDailyTrainingCount(trainingCount);

        // 5. 行管通知：需要提醒
        Long noticeCount = adminNoticeMapper.selectCount(new LambdaQueryWrapper<HotAdminNotice>()
                .eq(HotAdminNotice::getCompanyId, companyId)
                .and(w -> w.eq(HotAdminNotice::getIsReceived, 0L)
                        .or()
                        .eq(HotAdminNotice::getStatus, 2))
                .eq(HotAdminNotice::getIsDeleted, 0L));
        result.setAdminNoticeCount(noticeCount);

        return result;
    }

    @Override
    public HiddenDangerStatsVo getHiddenDangerStats() {
        LoginUser loginUser = LoginHelper.getLoginUser();
        if (loginUser == null) {
            return new HiddenDangerStatsVo();
        }
        Long companyId = loginUser.getCompanyId();
        HiddenDangerStatsVo result = new HiddenDangerStatsVo();
        List<HiddenDangerItemVo> enterpriseItems = new ArrayList<>();
        List<HiddenDangerItemVo> vehicleItems = new ArrayList<>();
        List<HiddenDangerItemVo> driverItems = new ArrayList<>();

        // 1. 获取所有有效的排查计划
        List<HotHiddenDangerPlan> plans = hiddenDangerPlanMapper.selectList(new LambdaQueryWrapper<HotHiddenDangerPlan>()
                .eq(HotHiddenDangerPlan::getCompanyId, companyId)
                .eq(HotHiddenDangerPlan::getIsDeleted, 0L));

        if (plans.isEmpty()) {
            result.setEnterpriseItems(enterpriseItems);
            result.setVehicleItems(vehicleItems);
            result.setDriverItems(driverItems);
            return result;
        }

        // 2. 遍历计划，查询对应的排查项目
        for (HotHiddenDangerPlan plan : plans) {
            List<HotHiddenDangerInspection> inspections = hiddenDangerInspectionMapper.selectList(new LambdaQueryWrapper<HotHiddenDangerInspection>()
                    .eq(HotHiddenDangerInspection::getPlanId, plan.getId())
                    .eq(HotHiddenDangerInspection::getCompanyId, companyId)
                    .in(HotHiddenDangerInspection::getStatus, Arrays.asList(1L, 2L))
                    .eq(HotHiddenDangerInspection::getIsDeleted, 0L));

            for (HotHiddenDangerInspection inspection : inspections) {
                Long type = inspection.getProjectType();
                Long status = inspection.getStatus();
                if (type == null || status == null) {
                    continue;
                }

                // 状态判断：1=特处理/待处理, 4=特处理 -> 待处理
                // 2=审核中, 5=审核中 -> 待审核
                boolean isPending = (status == 1L || status == 4L);
                boolean isAuditing = (status == 2L || status == 5L);

                if (!isPending && !isAuditing) {
                    continue;
                }

                HiddenDangerItemVo item = new HiddenDangerItemVo();
                item.setPlanName(plan.getPlanName());
                item.setDeadline(plan.getEndDate());
                item.setPlanId(plan.getId());
                item.setProjectType(type);
                item.setProjectId(inspection.getProjectId());
                item.setInspectionId(inspection.getId());
                item.setTargetName(inspection.getProjectName());

                String statusText = isPending ? "待处理" : "待审核";
                item.setStatus(isPending ? 1 : 2);

                String title = plan.getPlanName() + " " + inspection.getProjectName() + " " + statusText;
                item.setTitle(title);

                if (type == 1L) {
                    enterpriseItems.add(item);
                } else if (type == 2L) {
                    vehicleItems.add(item);
                } else if (type == 3L) {
                    driverItems.add(item);
                }
            }
        }

        result.setEnterpriseItems(enterpriseItems);
        result.setVehicleItems(vehicleItems);
        result.setDriverItems(driverItems);
        return result;
    }

    @Override
    public List<LearningStatsItemVo> getLearningStats(String type, String beginTime, String endTime) {
        LoginUser loginUser = LoginHelper.getLoginUser();
        if (loginUser == null) {
            return new ArrayList<>();
        }
        Long companyId = loginUser.getCompanyId();
        List<LearningStatsItemVo> result = new ArrayList<>();

        LocalDate today = LocalDate.now();
        LocalDate startDate;
        LocalDate endDate = today;

        if (StringUtils.isNotBlank(beginTime) && StringUtils.isNotBlank(endTime)) {
            startDate = LocalDate.parse(beginTime);
            endDate = LocalDate.parse(endTime);
        } else if ("week".equals(type)) {
            // 上周 (上周一到上周日)
            LocalDate lastWeek = today.minusWeeks(1);
            startDate = lastWeek.with(java.time.temporal.TemporalAdjusters.previousOrSame(java.time.DayOfWeek.MONDAY));
            endDate = lastWeek.with(java.time.temporal.TemporalAdjusters.nextOrSame(java.time.DayOfWeek.SUNDAY));
        } else if ("month".equals(type)) {
            // 上月 (上个月1号到上个月最后一天)
            LocalDate lastMonth = today.minusMonths(1);
            startDate = lastMonth.with(java.time.temporal.TemporalAdjusters.firstDayOfMonth());
            endDate = lastMonth.with(java.time.temporal.TemporalAdjusters.lastDayOfMonth());
        } else {
            // 默认返回上周
            LocalDate lastWeek = today.minusWeeks(1);
            startDate = lastWeek.with(java.time.temporal.TemporalAdjusters.previousOrSame(java.time.DayOfWeek.MONDAY));
            endDate = lastWeek.with(java.time.temporal.TemporalAdjusters.nextOrSame(java.time.DayOfWeek.SUNDAY));
        }

        Date start = Date.from(startDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
        Date end = Date.from(endDate.atTime(LocalTime.MAX).atZone(ZoneId.systemDefault()).toInstant());
        List<TrainingDailyStatsVo> rows = trainingDailyStatsMapper.selectByCompanyAndDateRange(companyId, start, end);
        Map<LocalDate, TrainingDailyStatsVo> rowMap = new HashMap<>();
        if (rows != null) {
            for (TrainingDailyStatsVo r : rows) {
                if (r.getStatsDate() != null) {
                    LocalDate d = r.getStatsDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
                    rowMap.put(d, r);
                }
            }
        }

        // 遍历每一天生成数据
        for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) {
            LearningStatsItemVo item = new LearningStatsItemVo();
//            if ("week".equals(type)) {
//                // 如果是周统计，返回星期几（如：星期一）
//                item.setDate(date.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL, Locale.CHINA));
//            } else {
//                item.setDate(date.toString());
//            }
            item.setDate(date.toString());
            TrainingDailyStatsVo r = rowMap.get(date);
            long completed = r != null && r.getCompletedCount() != null ? r.getCompletedCount() : 0L;
            long uncompleted = r != null && r.getUncompletedCount() != null ? r.getUncompletedCount() : 0L;
            item.setCompleted(completed);
            item.setIncomplete(uncompleted);
            result.add(item);
        }

        return result;
    }


    /**
     * 获取企业风险
     */
    private List<RiskCategoryVo> getCompanyRisk(Long companyId) {
        List<RiskCategoryVo> categories = new ArrayList<>();

        // 初始化分类
        RiskCategoryVo configCat = new RiskCategoryVo();
        configCat.setTitle("企业配置");
        configCat.setType("configuration");
        categories.add(configCat);

        RiskCategoryVo licenseCat = new RiskCategoryVo();
        licenseCat.setTitle("营业执照");
        licenseCat.setType("business_license");
        categories.add(licenseCat);

        RiskCategoryVo roadCat = new RiskCategoryVo();
        roadCat.setTitle("道路经营许可证");
        roadCat.setType("road_permit");
        categories.add(roadCat);

        RiskCategoryVo insuranceCat = new RiskCategoryVo();
        insuranceCat.setTitle("企业保险");
        insuranceCat.setType("company_insurance");
        categories.add(insuranceCat);

        RiskCategoryVo qualCat = new RiskCategoryVo();
        qualCat.setTitle("上岗资格证");
        qualCat.setType("qualification_certificate");
        categories.add(qualCat);

        RiskCategoryVo laborCat = new RiskCategoryVo();
        laborCat.setTitle("管理人员劳动合同");
        laborCat.setType("manager_labor_contract");
        categories.add(laborCat);

        // 1.1 企业配置检查
        List<RiskItemVo> configRisks = new ArrayList<>();

        // 车辆三检检查库
        HotVehicleThreeInspectConfigBo inspectConfigBo = new HotVehicleThreeInspectConfigBo();
        inspectConfigBo.setCompanyId(companyId);
        List<HotVehicleThreeInspectConfigVo> inspectConfigs = hotVehicleThreeInspectConfigService.queryList(inspectConfigBo);
        if (inspectConfigs.isEmpty()) {
            addRisk(configRisks, "车辆三检检查库: 未初始化", "not_configured", "企业配置", "configuration", "vehicleThreeInspect", "/config/vehicleThreeInspectConfig");
        }

        // 车辆三检检查项（检查是否配置了类型 1、2、3）
        // 由于配置结构是扁平的（每种类型一条记录），我们需要检查类型 1、2、3 是否存在且有内容。
        // 需求：“无记录 或 前/中/后任意内容为空”
        boolean hasFront = false, hasMiddle = false, hasBack = false;
        boolean contentComplete = true;
        for (HotVehicleThreeInspectConfigVo c : inspectConfigs) {
            if (c.getInspectType() != null) {
                if (c.getInspectType() == 1L) hasFront = true;
                if (c.getInspectType() == 2L) hasMiddle = true;
                if (c.getInspectType() == 3L) hasBack = true;
                if (StringUtils.isAnyBlank(c.getTitle(), c.getNormalText(), c.getAbnormalText())) {
                    contentComplete = false;
                }
            }
        }
        if (!hasFront || !hasMiddle || !hasBack || !contentComplete) {
            addRisk(configRisks, "车辆三检检查项: 未配置或内容缺失", "not_configured", "企业配置", "configuration", "InspectItemDialog", "/config/vehicleThreeInspectConfig");
        }

        // 隐患排查库
        HotHiddenDangerInspectStoreBo storeBo = new HotHiddenDangerInspectStoreBo();
        storeBo.setCompanyId(companyId);
        List<HotHiddenDangerInspectStoreVo> storeItems = hotHiddenDangerInspectStoreService.queryList(storeBo);
        if (storeItems.isEmpty()) {
            addRisk(configRisks, "隐患排查库: 未初始化", "not_configured", "企业配置", "configuration", "hiddenDangerLib", "/config/hiddenDangerInspectStore");
        }

        // 隐患排查检查项
        HotHiddenDangerCheckConfigBo checkConfigBo = new HotHiddenDangerCheckConfigBo();
        checkConfigBo.setCompanyId(companyId);
        List<HotHiddenDangerCheckConfigVo> checkConfigs = hotHiddenDangerCheckConfigService.queryList(checkConfigBo);
        if (checkConfigs.isEmpty()) {
            addRisk(configRisks, "隐患排查检查项: 未配置", "not_configured", "企业配置", "configuration", "hiddenDanger", "/config/hiddenDangerCheckConfig");
        } else {
            // 检查是否存在默认项且内容不为空
            // 需求：“无默认项记录 或 企业/车辆/人员检查内容任意一项为空”
            boolean hasDefault = false;
            boolean contentValid = false;
            for (HotHiddenDangerCheckConfigVo c : checkConfigs) {
                if (c.getIsDefault() != null && c.getIsDefault() == 1L) {
                    hasDefault = true;
                    if (StringUtils.isNotBlank(c.getCompanyInspectJson()) &&
                            StringUtils.isNotBlank(c.getVehicleInspectJson()) &&
                            StringUtils.isNotBlank(c.getPersonInspectJson())) {
                        contentValid = true;
                    }
                    break;
                }
            }

            if (!hasDefault) {
                addRisk(configRisks, "隐患排查检查项: 默认项未配置", "not_configured", "企业配置", "configuration", "hiddenDanger", "/config/hiddenDangerCheckConfig");
            } else if (!contentValid) {
                addRisk(configRisks, "隐患排查检查项: 检查内容缺失", "not_configured", "企业配置", "configuration", "hiddenDanger", "/config/hiddenDangerCheckConfig");
            }
        }

        // 车辆年审提醒配置
        HotVehicleInspectionConfigBo viBo = new HotVehicleInspectionConfigBo();
        viBo.setCompanyId(companyId);
        List<HotVehicleInspectionConfigVo> viConfigs = hotVehicleInspectionConfigService.queryList(viBo);
        boolean viEnabledExists = viConfigs != null && viConfigs.stream().anyMatch(c -> c.getIsEnabled() == null || c.getIsEnabled() == 1L);
        if (viConfigs == null || viConfigs.isEmpty() || !viEnabledExists) {
            addRisk(configRisks, "车辆年审提醒: 未设置", "not_configured", "企业配置", "configuration", "annualInspection", "/config/vehicleInspectionConfig");
        }

        // 车辆二级维护提醒配置
        HotVehicleSecondaryMaintenanceConfigBo smBo = new HotVehicleSecondaryMaintenanceConfigBo();
        smBo.setCompanyId(companyId);
        List<HotVehicleSecondaryMaintenanceConfigVo> smConfigs = hotVehicleSecondaryMaintenanceConfigService.queryList(smBo);
        boolean smEnabledExists = smConfigs != null && smConfigs.stream().anyMatch(c -> c.getIsEnabled() == null || c.getIsEnabled() == 1L);
        if (smConfigs == null || smConfigs.isEmpty() || !smEnabledExists) {
            addRisk(configRisks, "车辆二级维护提醒: 未设置", "not_configured", "企业配置", "configuration", "secondaryMaintenance", "/config/vehicleSecondaryMaintenanceConfig");
        }
        // 隐患排查期限
        HotCompanyBasicConfigVo basicConfig = null;
        try {
            HotCompanyBasicConfigBo basicConfigBo = new HotCompanyBasicConfigBo();
            basicConfigBo.setCompanyId(companyId);
            List<HotCompanyBasicConfigVo> basicConfigs = hotCompanyBasicConfigService.queryList(basicConfigBo);
            if (!basicConfigs.isEmpty()) {
                basicConfig = basicConfigs.get(0);
            }
        } catch (Exception e) {
            // 忽略
        }

        if (basicConfig == null || basicConfig.getHiddenDangerRole() == null) {
            addRisk(configRisks, "隐患排查期限: 未配置", "not_configured", "企业配置", "configuration", "hiddenDangerRole", "/config/companyBasicConfig");
        }
        configCat.setItems(configRisks);
        configCat.setCount(configRisks.size());

        // 1.2 营业执照（预警阈值 60 天）
        List<RiskItemVo> licenseRisks = new ArrayList<>();
        List<RiskItemVo> roadRisks = new ArrayList<>();
        SysCompanyVo company = sysCompanyService.queryById(companyId);
        long licenseWarningDays = 60;

        if (company != null) {
            // 营业执照
            boolean isLongTerm = company.getBusinessLongTerm() != null && company.getBusinessLongTerm();
            if (!isLongTerm) {
                checkDateRisk(licenseRisks, company.getBusinessTerm(), "营业执照", "营业执照", "business_license", licenseWarningDays, null, null, null, "/resourceManagement/company");
            }

            // 道路经营许可证（预警阈值 60 天）
            checkDateRisk(roadRisks, company.getRoadExpireDate(), "道路经营许可证", "道路经营许可证", "road_permit", licenseWarningDays, null, null, null, "/resourceManagement/company");
        }
        licenseCat.setItems(licenseRisks);
        licenseCat.setCount(licenseRisks.size());
        roadCat.setItems(roadRisks);
        roadCat.setCount(roadRisks.size());

        // 1.3 企业保险（预警阈值 60 天）
        List<RiskItemVo> insuranceRisks = new ArrayList<>();
        HotCompanyInsuranceBo insuranceBo = new HotCompanyInsuranceBo();
        insuranceBo.setCompanyId(companyId);
        // 只查询未归档的保险
        insuranceBo.setIsArchived(0L);
        List<HotCompanyInsuranceVo> insurances = hotCompanyInsuranceService.queryList(insuranceBo);

        for (HotCompanyInsuranceVo ins : insurances) {
            String insuranceName = StringUtils.isNotBlank(ins.getInsuranceType()) ? ins.getInsuranceType() : "未知";
            String dateStr = ins.getEndDate() != null ? convertDateToLocalDate(ins.getEndDate()).toString() : "";
            checkDateRisk(insuranceRisks, convertDateToLocalDate(ins.getEndDate()), "企业保险: (" + insuranceName + ") " + dateStr, "企业保险", "company_insurance", licenseWarningDays, null, null, String.valueOf(ins.getId()), "/resourceManagement/companyInsurance");
        }

        insuranceCat.setItems(insuranceRisks);
        insuranceCat.setCount(insuranceRisks.size());

        // 1.4 管理人员（预警阈值 60 天）
        List<RiskItemVo> qualRisks = new ArrayList<>();
        List<RiskItemVo> laborRisks = new ArrayList<>();
        long contractWarningDays = 30; // 劳动合同预警阈值

        HotCompanySafetyManagerBo managerBo = new HotCompanySafetyManagerBo();
        managerBo.setCompanyId(companyId);
        managerBo.setStatus(1L); // 在职
        managerBo.setIsResigned(0L); // 未离职
        List<HotCompanySafetyManagerVo> managers = hotCompanySafetyManagerService.queryList(managerBo);
        for (HotCompanySafetyManagerVo m : managers) {
            boolean isCorpHead = m.getCompanyHead() != null && m.getCompanyHead() == 1L;
            if (isCorpHead) {
                continue;
            }
            // 上岗资格证（预警阈值 60 天）
            if (StringUtils.isBlank(m.getCertificateNo())) {
                checkDateRisk(qualRisks, convertDateToLocalDate(m.getCertificateExpireDate()), "上岗资格证: (" + m.getName() + ")", "上岗资格证", "qualification_certificate", licenseWarningDays, 2, String.valueOf(m.getId()), null, "/resourceManagement/companySafetyManager");
            } else {
                checkDateRisk(qualRisks, convertDateToLocalDate(m.getCertificateExpireDate()), "上岗资格证: (" + m.getName() + ")", "上岗资格证", "qualification_certificate", licenseWarningDays, 2, String.valueOf(m.getId()), null, "/resourceManagement/companySafetyManager");
            }

            // 劳动合同（预警阈值 30 天）
            // 目标：在职且合同未归档
            HotCompanyLaborContractBo contractBo = new HotCompanyLaborContractBo();
            contractBo.setCompanyId(companyId);
            contractBo.setSafeUserId(m.getId());
            // 过滤掉已归档的劳动合同
            contractBo.setIsArchived(0L);
            List<HotCompanyLaborContractVo> contracts = hotCompanyLaborContractService.queryList(contractBo);

            boolean hasValidContract = false;
            for (HotCompanyLaborContractVo c : contracts) {
                hasValidContract = true;
                checkDateRisk(laborRisks, convertDateToLocalDate(c.getExpireDate()), "劳动合同: (" + m.getName() + ")", "管理人员劳动合同", "manager_labor_contract", contractWarningDays, 2, String.valueOf(m.getId()), String.valueOf(c.getId()), "/resourceManagement/companyLaborContract");
            }
            if (!hasValidContract) {
                addRisk(laborRisks, "劳动合同: (" + m.getName() + ") 未签署或未归档", "not_configured", "管理人员劳动合同", "manager_labor_contract", 2, String.valueOf(m.getId()), null, "/resourceManagement/companyLaborContract");
            }
        }

        qualCat.setItems(qualRisks);
        qualCat.setCount(qualRisks.size());
        laborCat.setItems(laborRisks);
        laborCat.setCount(laborRisks.size());

        return categories;
    }

    private List<RiskCategoryVo> getVehicleRisk(Long companyId) {
        List<RiskCategoryVo> categories = new ArrayList<>();
        long warningDays = 30;

        RiskCategoryVo licenseInspectionCat = new RiskCategoryVo();
        licenseInspectionCat.setTitle("行驶证检验有效期");
        licenseInspectionCat.setType("vehicle_license_inspection_validity");
        categories.add(licenseInspectionCat);

        RiskCategoryVo scrapCat = new RiskCategoryVo();
        scrapCat.setTitle("行驶证强制报废期");
        scrapCat.setType("vehicle_mandatory_scrap");
        categories.add(scrapCat);

        RiskCategoryVo transportCat = new RiskCategoryVo();
        transportCat.setTitle("运输证");
        transportCat.setType("transport_permit");
        categories.add(transportCat);

        RiskCategoryVo contractCat = new RiskCategoryVo();
        contractCat.setTitle("车辆合同");
        contractCat.setType("vehicle_contract");
        categories.add(contractCat);

        RiskCategoryVo insuranceCat = new RiskCategoryVo();
        insuranceCat.setTitle("车辆保险");
        insuranceCat.setType("vehicle_insurance");
        categories.add(insuranceCat);

        RiskCategoryVo terminalCat = new RiskCategoryVo();
        terminalCat.setTitle("定位终端安装");
        terminalCat.setType("vehicle_terminal_install");
        categories.add(terminalCat);

        RiskCategoryVo annualCheckCat = new RiskCategoryVo();
        annualCheckCat.setTitle("年审检查");
        annualCheckCat.setType("vehicle_annual_check");
        categories.add(annualCheckCat);

        RiskCategoryVo secondaryMaintenanceCat = new RiskCategoryVo();
        secondaryMaintenanceCat.setTitle("二级维护");
        secondaryMaintenanceCat.setType("vehicle_secondary_maintenance");
        categories.add(secondaryMaintenanceCat);

        List<RiskItemVo> licenseInspectionRisks = new ArrayList<>();
        List<RiskItemVo> scrapRisks = new ArrayList<>();
        List<RiskItemVo> transportRisks = new ArrayList<>();
        List<RiskItemVo> contractRisks = new ArrayList<>();
        List<RiskItemVo> insuranceRisks = new ArrayList<>();
        List<RiskItemVo> terminalRisks = new ArrayList<>();
        List<RiskItemVo> annualCheckRisks = new ArrayList<>();
        List<RiskItemVo> secondaryMaintenanceRisks = new ArrayList<>();

        HotVehicleBo vehicleBo = new HotVehicleBo();
        vehicleBo.setCompanyId(companyId);
        // 仅检查正常运营车辆
        vehicleBo.setVehicleStatus(1L);
        vehicleBo.setOperationStatus(1L); // 1=正常
        List<HotVehicleVo> vehicles = hotVehicleService.queryList(vehicleBo);

        HotVehicleContractBo contractBo = new HotVehicleContractBo();
        contractBo.setCompanyId(companyId);
        List<HotVehicleContractVo> allContracts = hotVehicleContractService.queryList(contractBo);
        Map<String, HotVehicleContractVo> contractMap = new HashMap<>();
        if (allContracts != null) {
            for (HotVehicleContractVo c : allContracts) {
                if (c == null || StringUtils.isBlank(c.getVehicleId()) || (c.getIsDeleted() != null && c.getIsDeleted() == 1L)) {
                    continue;
                }
                HotVehicleContractVo prev = contractMap.get(c.getVehicleId());
                if (prev == null) {
                    contractMap.put(c.getVehicleId(), c);
                } else {
                    Date prevDate = prev.getExpireDate();
                    Date curDate = c.getExpireDate();
                    if (prevDate == null) {
                        if (curDate != null) {
                            contractMap.put(c.getVehicleId(), c);
                        }
                    } else if (curDate != null && curDate.after(prevDate)) {
                        contractMap.put(c.getVehicleId(), c);
                    }
                }
            }
        }

        HotVehicleTerminalInstallBo terminalBo = new HotVehicleTerminalInstallBo();
        terminalBo.setCompanyId(companyId);
        List<HotVehicleTerminalInstallVo> allTerminalInstalls = hotVehicleTerminalInstallService.queryList(terminalBo);
        Map<String, HotVehicleTerminalInstallVo> terminalMap = new HashMap<>();
        if (allTerminalInstalls != null) {
            for (HotVehicleTerminalInstallVo t : allTerminalInstalls) {
                if (t == null || StringUtils.isBlank(t.getVehicleId()) || (t.getIsDeleted() != null && t.getIsDeleted() == 1L)) {
                    continue;
                }
                HotVehicleTerminalInstallVo prev = terminalMap.get(t.getVehicleId());
                if (prev == null) {
                    terminalMap.put(t.getVehicleId(), t);
                } else {
                    Date prevDate = prev.getServiceEndDate();
                    Date curDate = t.getServiceEndDate();
                    if (prevDate == null) {
                        if (curDate != null) {
                            terminalMap.put(t.getVehicleId(), t);
                        }
                    } else if (curDate != null && curDate.after(prevDate)) {
                        terminalMap.put(t.getVehicleId(), t);
                    }
                }
            }
        }

        HotVehicleInspectionConfigBo annualConfigBo = new HotVehicleInspectionConfigBo();
        annualConfigBo.setCompanyId(companyId);
        annualConfigBo.setIsEnabled(1L);
        annualConfigBo.setIsDeleted(0L);
        List<HotVehicleInspectionConfigVo> annualConfigs = hotVehicleInspectionConfigService.queryList(annualConfigBo);
        List<VehicleUtils.AnnualInspectionRule> annualRules = new ArrayList<>();
        if (annualConfigs != null) {
            annualRules = annualConfigs.stream()
                    .filter(c -> c != null && (c.getIsEnabled() == null || c.getIsEnabled() == 1L))
                    .filter(c -> c.getIntervalYears() != null && c.getInspectionTimes() != null)
                    .sorted(Comparator.comparing(c -> c.getIntervalYears() == null ? Long.MAX_VALUE : c.getIntervalYears()))
                    .map(c -> new VehicleUtils.AnnualInspectionRule(c.getIntervalYears().intValue(), c.getInspectionTimes().intValue()))
                    .collect(Collectors.toList());
        }

        HotVehicleSecondaryMaintenanceConfigBo secondaryConfigBo = new HotVehicleSecondaryMaintenanceConfigBo();
        secondaryConfigBo.setCompanyId(companyId);
        secondaryConfigBo.setIsEnabled(1L);
        secondaryConfigBo.setIsDeleted(0L);
        List<HotVehicleSecondaryMaintenanceConfigVo> secondaryConfigs = hotVehicleSecondaryMaintenanceConfigService.queryList(secondaryConfigBo);
        List<VehicleUtils.AnnualInspectionRule> secondaryRules = new ArrayList<>();
        if (secondaryConfigs != null) {
            secondaryRules = secondaryConfigs.stream()
                    .filter(c -> c != null && (c.getIsEnabled() == null || c.getIsEnabled() == 1L))
                    .filter(c -> c.getIntervalYears() != null && c.getInspectionTimes() != null)
                    .sorted(Comparator.comparing(c -> c.getIntervalYears() == null ? Long.MAX_VALUE : c.getIntervalYears()))
                    .map(c -> new VehicleUtils.AnnualInspectionRule(c.getIntervalYears().intValue(), c.getInspectionTimes().intValue()))
                    .collect(Collectors.toList());
        }

        for (HotVehicleVo v : vehicles) {
            String link = "/resourceManagement/vehicleManagement/detail?id=" + v.getId();
            LocalDate registrationDate = convertDateToLocalDate(v.getCertificateIssueDate());

            LocalDate licenseInspectionDate = parseYearMonthEnd(v.getInspectionValidMonth());
            if (licenseInspectionDate == null) {
                addRisk(licenseInspectionRisks, "行驶证检验有效期: (" + v.getPlateNumber() + ") 未填写", "not_filled", "行驶证检验有效期", "vehicle_license_inspection_validity", null, null, String.valueOf(v.getId()), link);
            } else {
                checkDateRisk(licenseInspectionRisks, licenseInspectionDate, "行驶证检验有效期: (" + v.getPlateNumber() + ")", "行驶证检验有效期", "vehicle_license_inspection_validity", warningDays, null, null, String.valueOf(v.getId()), link);
            }

            LocalDate scrapDate = convertDateToLocalDate(v.getMandatoryScrapEndDate());
            if (scrapDate == null) {
                addRisk(scrapRisks, "行驶证强制报废期: (" + v.getPlateNumber() + ") 未填写", "not_filled", "行驶证强制报废期", "vehicle_mandatory_scrap", null, null, String.valueOf(v.getId()), link);
            } else {
                checkDateRisk(scrapRisks, scrapDate, "行驶证强制报废期: (" + v.getPlateNumber() + ")", "行驶证强制报废期", "vehicle_mandatory_scrap", warningDays, null, null, String.valueOf(v.getId()), link);
            }

            LocalDate transportDate = parseYearMonthEnd(v.getTransportValidMonth());
            if (transportDate == null) {
                addRisk(transportRisks, "运输证: (" + v.getPlateNumber() + ") 未填写", "not_filled", "运输证", "transport_permit", null, null, String.valueOf(v.getId()), link);
            } else {
                checkDateRisk(transportRisks, transportDate, "运输证: (" + v.getPlateNumber() + ")", "运输证", "transport_permit", warningDays, null, null, String.valueOf(v.getId()), link);
            }

            HotVehicleContractVo contract = contractMap.get(v.getId());
            LocalDate contractDate = contract == null ? null : convertDateToLocalDate(contract.getExpireDate());
            if (contractDate == null) {
                addRisk(contractRisks, "车辆合同: (" + v.getPlateNumber() + ") 未填写", "not_filled", "车辆合同", "vehicle_contract", null, null, String.valueOf(v.getId()), link);
            } else {
                checkDateRisk(contractRisks, contractDate, "车辆合同: (" + v.getPlateNumber() + ")", "车辆合同", "vehicle_contract", warningDays, null, null, String.valueOf(v.getId()), link);
            }

            HotVehicleInsuranceBo insuranceBo = new HotVehicleInsuranceBo();
            insuranceBo.setVehicleId(v.getId());
            // 过滤掉已归档的保险
            insuranceBo.setIsArchived(0);
            List<HotVehicleInsuranceVo> insurances = hotVehicleInsuranceService.queryList(insuranceBo);
            if (insurances.isEmpty()) {
                addRisk(insuranceRisks, "车辆保险: (" + v.getPlateNumber() + ") 未配置", "not_configured", "车辆保险", "vehicle_insurance", null, null, String.valueOf(v.getId()), link);
            } else {
                boolean hasNullEndDate = false;
                for (HotVehicleInsuranceVo ins : insurances) {
                    LocalDate endDate = convertDateToLocalDate(ins == null ? null : ins.getEndDate());
                    if (endDate == null) {
                        hasNullEndDate = true;
                        continue;
                    }
                    checkDateRisk(insuranceRisks, endDate, "车辆保险: (" + v.getPlateNumber() + ")", "车辆保险", "vehicle_insurance", warningDays, null, null, String.valueOf(v.getId()), link);
                }
                if (hasNullEndDate) {
                    addRisk(insuranceRisks, "车辆保险: (" + v.getPlateNumber() + ") 未填写", "not_filled", "车辆保险", "vehicle_insurance", null, null, String.valueOf(v.getId()), link);
                }
            }

            // 从预处理的 terminalMap 中获取该车辆“定位终端安装”的最新记录（按 serviceEndDate 取最近的一条）
            HotVehicleTerminalInstallVo terminal = terminalMap.get(v.getId());
            // 将服务截止日期转换为 LocalDate，作为到期/过期判断的基准日期
            LocalDate terminalDate = terminal == null ? null : convertDateToLocalDate(terminal.getServiceEndDate());
            if (terminalDate == null) {
                // 无记录或截止日期未填写，提示信息不全
                addRisk(terminalRisks, "定位终端安装: (" + v.getPlateNumber() + ") 未填写", "not_filled", "定位终端安装", "vehicle_terminal_install", null, null, String.valueOf(v.getId()), link);
            } else {
                // 有截止日期：按 warningDays 做到期/过期预警
                checkDateRisk(terminalRisks, terminalDate, "定位终端安装: (" + v.getPlateNumber() + ")", "定位终端安装", "vehicle_terminal_install", warningDays, null, null, String.valueOf(v.getId()), link);
            }

            HotVehicleAnnualReviewVo annual = hotVehicleAnnualReviewService.queryLatestByVehicleId(v.getId());
            LocalDate lastInspectionDate = annual == null ? null : convertDateToLocalDate(annual.getReviewDate());
            VehicleUtils.InspectionResult result1 = VehicleUtils.calculateInspectionStatus(annualRules, registrationDate, lastInspectionDate);
            if (result1 != null) {
                if (result1.getOverdueDays() > 0 || (result1.getRemainingDays() == 0 && result1.getWarningLevel() == 1)) {
                    addRisk(annualCheckRisks, "年审检测：（" + v.getPlateNumber() + "） 已到期（" + result1.getOverdueDays() + "）天", "expired", "年审检测", "vehicle_annual_check", null, null, String.valueOf(v.getId()), link);
                } else if (result1.getRemainingDays() > 0 && result1.getRemainingDays() <= 30) {
                    addRisk(annualCheckRisks, "年审检测：（" + v.getPlateNumber() + "） 剩余（" + result1.getRemainingDays() + "）天到期", "warning", "年审检测", "vehicle_annual_check", null, null, String.valueOf(v.getId()), link);
                }
            }

            HotVehicleMaintenanceVo maintenance = hotVehicleMaintenanceService.queryLatestByVehicleId(v.getId());
            LocalDate maintenanceBaseDate = maintenance == null ? null : convertDateToLocalDate(maintenance.getFinishTime() != null ? maintenance.getFinishTime() : maintenance.getMaintainDate());
            VehicleUtils.InspectionResult result2 = VehicleUtils.calculateMaintenanceStatus(secondaryRules, registrationDate, maintenanceBaseDate);
            if (result2 != null) {
                if (result2.getOverdueDays() > 0 || (result2.getRemainingDays() == 0 && result2.getWarningLevel() == 1)) {
                    addRisk(secondaryMaintenanceRisks, "二级维护：（" + v.getPlateNumber() + "） 已到期（" + result2.getOverdueDays() + "）天", "expired", "二级维护", "vehicle_secondary_maintenance", null, null, String.valueOf(v.getId()), link);
                } else if (result2.getRemainingDays() > 0 && result2.getRemainingDays() <= 30) {
                    addRisk(secondaryMaintenanceRisks, "二级维护：（" + v.getPlateNumber() + "） 剩余（" + result2.getRemainingDays() + "）天到期", "warning", "二级维护", "vehicle_secondary_maintenance", null, null, String.valueOf(v.getId()), link);
                }
            }
        }

        licenseInspectionCat.setItems(licenseInspectionRisks);
        licenseInspectionCat.setCount(licenseInspectionRisks.size());
        scrapCat.setItems(scrapRisks);
        scrapCat.setCount(scrapRisks.size());
        transportCat.setItems(transportRisks);
        transportCat.setCount(transportRisks.size());
        contractCat.setItems(contractRisks);
        contractCat.setCount(contractRisks.size());
        insuranceCat.setItems(insuranceRisks);
        insuranceCat.setCount(insuranceRisks.size());
        terminalCat.setItems(terminalRisks);
        terminalCat.setCount(terminalRisks.size());
        annualCheckCat.setItems(annualCheckRisks);
        annualCheckCat.setCount(annualCheckRisks.size());
        secondaryMaintenanceCat.setItems(secondaryMaintenanceRisks);
        secondaryMaintenanceCat.setCount(secondaryMaintenanceRisks.size());

        return categories;
    }

    private List<RiskCategoryVo> getDriverRisk(Long companyId) {
        List<RiskCategoryVo> categories = new ArrayList<>();
        // 初始化分类
        RiskCategoryVo idCat = new RiskCategoryVo();
        idCat.setTitle("身份证");
        idCat.setType("id_card");
        categories.add(idCat);

        RiskCategoryVo promiseCat = new RiskCategoryVo();
        promiseCat.setTitle("承诺书");
        promiseCat.setType("commitment_letter");
        categories.add(promiseCat);

        RiskCategoryVo dlCat = new RiskCategoryVo();
        dlCat.setTitle("驾驶证");
        dlCat.setType("driver_license");
        categories.add(dlCat);

        RiskCategoryVo qualCat = new RiskCategoryVo();
        qualCat.setTitle("从业资格证");
        qualCat.setType("qualification_certificate");
        categories.add(qualCat);

        RiskCategoryVo laborCat = new RiskCategoryVo();
        laborCat.setTitle("劳动合同");
        laborCat.setType("labor_contract");
        categories.add(laborCat);

        // 风险项列表
        List<RiskItemVo> idRisks = new ArrayList<>();
        List<RiskItemVo> promiseRisks = new ArrayList<>();
        List<RiskItemVo> dlRisks = new ArrayList<>();
        List<RiskItemVo> qualRisks = new ArrayList<>();
        List<RiskItemVo> laborRisks = new ArrayList<>();

        // 获取驾驶员列表
        HotDriverBo driverBo = new HotDriverBo();
        driverBo.setCompanyId(companyId);
        driverBo.setStatus(1L); // 正常状态
        driverBo.setAuditStatus(1);
        List<HotDriverVo> drivers = hotDriverService.queryList(driverBo);

        // 获取合同列表
        HotDriverLaborContractBo contractBo = new HotDriverLaborContractBo();
        contractBo.setCompanyId(companyId);
        List<HotDriverLaborContractVo> allContracts = hotDriverLaborContractService.queryList(contractBo);
        Map<String, List<HotDriverLaborContractVo>> contractMap = allContracts.stream()
                .collect(Collectors.groupingBy(HotDriverLaborContractVo::getDriverId));

        for (HotDriverVo d : drivers) {
            if (d.getId() == null) continue;
            String driverId = d.getId();
            String name = d.getName();
            String linkBase = "/driver/info?id=" + driverId + "&type=";
            boolean isDriver = "1".equals(d.getPersonType()) || "driver".equalsIgnoreCase(d.getPersonType()); // 假设 "1" 或 "driver" 代表驾驶员

            // 1. 身份证
            boolean idCardLongTerm = d.getIdCardLongTerm() != null && d.getIdCardLongTerm() == 1L;
            if (!idCardLongTerm) {
                if (d.getIdCardExpireDate() == null) {
                    addRisk(idRisks, "身份证: (" + name + ") 到期时间未填写", "not_filled", "身份证", "id_card", 1, driverId, null, linkBase + "1");
                } else {
                    checkDateRisk(idRisks, convertDateToLocalDate(d.getIdCardExpireDate()), "身份证: (" + name + ")", "身份证", "id_card", 90, 1, driverId, null, linkBase + "1");
                }
            }

            // 2. 承诺书
            if (d.getIntegrityPromiseExpireDate() == null || StringUtils.isBlank(d.getIntegrityLevel()) || StringUtils.isBlank(d.getIntegrityPromiseAttachmentUrl())) {
                addRisk(promiseRisks, "承诺书: (" + name + ") 信息不全", "not_filled", "承诺书", "commitment_letter", 1, driverId, null, linkBase + "1");
            } else {
                checkDateRisk(promiseRisks, convertDateToLocalDate(d.getIntegrityPromiseExpireDate()), "承诺书: (" + name + ")", "承诺书", "commitment_letter", 30, 1, driverId, null, linkBase + "1");
            }

            // 3. 驾驶证
//            if (StringUtils.isBlank(d.getDriverLicenseNo())) {
//                // 仅在人员类型为驾驶员时预警
//                if (isDriver) {
//                    addRisk(dlRisks, "驾驶证: (" + name + ") 到期时间未填写", "not_filled", "驾驶证", "driver_license", 1, driverId, null, linkBase + "1");
//                }
//            } else {
            boolean isLongTerm = d.getDriverLicenseLongTerm() != null && d.getDriverLicenseLongTerm() == 1L;
            if (!isLongTerm && isDriver) {
                checkDateRisk(dlRisks, convertDateToLocalDate(d.getDriverLicenseExpireDate()), "驾驶证: (" + name + ")", "驾驶证", "driver_license", 90, 1, driverId, null, linkBase + "1");
            }
//            }

            // 4. 从业资格证
            if (d.getQualificationValidEndDate() != null) {
                checkDateRisk(qualRisks, convertDateToLocalDate(d.getQualificationValidEndDate()), "从业资格证: (" + name + ")", "从业资格证", "qualification_certificate", 90, 1, driverId, null, linkBase + "1");
            } else {
                // 如果不是驾驶员（如押运员）且缺失，则预警
                if (!isDriver) {
                    addRisk(qualRisks, "从业资格证: (" + name + ") 有效期未填写", "not_filled", "从业资格证", "qualification_certificate", 1, driverId, null, linkBase + "1");
                }
            }

            // 5. 劳动合同
            List<HotDriverLaborContractVo> contracts = contractMap.getOrDefault(d.getId(), Collections.emptyList());
            boolean hasContract = false;
            for (HotDriverLaborContractVo c : contracts) {
                if ((c.getIsDeleted() != null && c.getIsDeleted() == 1L) || (c.getIsArchived() != null && c.getIsArchived() == 1L))
                    continue;
                hasContract = true;
                if (c.getExpireDate() == null) {
                    addRisk(laborRisks, "劳动合同: (" + name + ") 截至时间未填写", "not_filled", "劳动合同", "labor_contract", 1, driverId, String.valueOf(c.getId()), linkBase + "5");
                } else {
                    checkDateRisk(laborRisks, convertDateToLocalDate(c.getExpireDate()), "劳动合同: (" + name + ")", "劳动合同", "labor_contract", 30, 1, driverId, String.valueOf(c.getId()), linkBase + "5");
                }
            }
            if (!hasContract) {
                addRisk(laborRisks, "劳动合同: (" + name + ") 未签署", "not_signed", "劳动合同", "labor_contract", 1, driverId, null, linkBase + "5");
            }
        }

        idCat.setItems(idRisks);
        idCat.setCount(idRisks.size());
        promiseCat.setItems(promiseRisks);
        promiseCat.setCount(promiseRisks.size());
        dlCat.setItems(dlRisks);
        dlCat.setCount(dlRisks.size());
        qualCat.setItems(qualRisks);
        qualCat.setCount(qualRisks.size());
        laborCat.setItems(laborRisks);
        laborCat.setCount(laborRisks.size());

        return categories;
    }

    private List<RiskCategoryVo> getTrainingRisk(Long companyId) {
        List<RiskCategoryVo> categories = new ArrayList<>();

        // 1. 获取有效计划
        HotTrainingBo trainingBo = new HotTrainingBo();
        trainingBo.setCompanyId(companyId);
        List<HotTrainingVo> allTrainings = hotTrainingService.queryList(trainingBo);

        List<HotTrainingVo> validTrainings = allTrainings.stream()
                .filter(t -> {
                    if (t == null) {
                        return false;
                    }
                    if ("offline".equals(t.getTrainingType())) {
                        return false;
                    }
                    return t.getIsEnabled() != null && t.getIsEnabled() == 1L;
                })
                .collect(Collectors.toList());

        // 按类型分组
        Map<String, List<HotTrainingVo>> trainingMap = validTrainings.stream()
                .collect(Collectors.groupingBy(t -> t.getTrainingType() != null ? t.getTrainingType() : "other"));

        // 2. 获取在职驾驶员以检查覆盖率
        HotDriverBo driverBo = new HotDriverBo();
        driverBo.setCompanyId(companyId);
        driverBo.setStatus(1L);
        driverBo.setAuditStatus(1);
        List<HotDriverVo> drivers = hotDriverService.queryList(driverBo);

        // 3. 处理每种类型
        // 关注的预定义类型

        // 单独处理岗前培训
        processPreJobRisk(companyId, trainingMap, drivers, categories);

        List<String> orderedTypes = List.of(
                "daily",
                "accident",
                "violation",
                "emergency",
                "prevention",
                "education"
        );

        for (String type : orderedTypes) {
            List<HotTrainingVo> typeList = new ArrayList<>(trainingMap.getOrDefault(type, List.of()));

            List<RiskItemVo> risks = new ArrayList<>();
            RiskCategoryVo cat = new RiskCategoryVo();
            cat.setTitle(getTrainingTypeLabel(type));
            cat.setType(type);

            for (HotTrainingVo t : typeList) {
                if (t == null) {
                    continue;
                }
                if (t.getEndTime() != null) {
                    checkDateRisk(risks, convertDateToLocalDate(t.getEndTime()), "培训计划: (" + t.getPlanName() + ")", cat.getTitle(), type, 30, null, null, String.valueOf(t.getId()), "/securityManagement/training", String.valueOf(t.getId()), null);
                }

                HotTrainingParticipantBo partBo = new HotTrainingParticipantBo();
                partBo.setTrainingId(t.getId());
                List<HotTrainingParticipantVo> participants = hotTrainingParticipantService.queryList(partBo);

                for (HotTrainingParticipantVo p : participants) {
                    boolean completed = (p.getIsCompleted() != null && p.getIsCompleted() == 1L)
                            || (p.getLearnProgress() != null && p.getLearnProgress().compareTo(BigDecimal.valueOf(100)) >= 0);
                    if (!completed) {
                        addRisk(risks, "培训未完成: (" + p.getUserName() + ") - " + t.getPlanName(), "not_completed", cat.getTitle(), type, null, p.getUserId(), String.valueOf(t.getId()), "/securityManagement/training", String.valueOf(t.getId()), String.valueOf(p.getId()));
                    }
                }
            }
            cat.setItems(risks);
            cat.setCount(risks.size());
            categories.add(cat);
        }

        return categories;
    }

    private void processPreJobRisk(Long companyId, Map<String, List<HotTrainingVo>> trainingMap, List<HotDriverVo> drivers, List<RiskCategoryVo> categories) {
        RiskCategoryVo cat = new RiskCategoryVo();
        cat.setTitle("岗前培训");
        cat.setType("pre_job");
        List<RiskItemVo> risks = new ArrayList<>();

        List<HotTrainingVo> preJobPlans = trainingMap.getOrDefault("pre-job", new ArrayList<>());
        for (HotTrainingVo t : preJobPlans) {
            if (t == null) {
                continue;
            }
            if (t.getEndTime() != null) {
                checkDateRisk(risks, convertDateToLocalDate(t.getEndTime()), "培训计划: (" + t.getPlanName() + ")", "岗前培训", "pre_job", 30, null, null, String.valueOf(t.getId()), "/securityManagement/training", String.valueOf(t.getId()), null);
            }
        }

        // 收集线上培训参与者
        List<Long> onlinePlanIds = new ArrayList<>();
        preJobPlans.forEach(p -> onlinePlanIds.add(p.getId()));

        List<HotTrainingParticipantVo> onlineParticipants = new ArrayList<>();
        if (!onlinePlanIds.isEmpty()) {
            for (Long pid : onlinePlanIds) {
                HotTrainingParticipantBo bo = new HotTrainingParticipantBo();
                bo.setTrainingId(pid);
                onlineParticipants.addAll(hotTrainingParticipantService.queryList(bo));
            }
        }

        // 手机号 -> 线上参与者映射
        Map<String, List<HotTrainingParticipantVo>> onlinePartMap = onlineParticipants.stream()
                .filter(p -> StringUtils.isNotBlank(p.getPhone()))
                .collect(Collectors.groupingBy(HotTrainingParticipantVo::getPhone));

        Set<String> offlinePreJobUserIds = new HashSet<>();
        if (companyId != null) {
            HotTrainingBo offlinePreJobBo = new HotTrainingBo();
            offlinePreJobBo.setCompanyId(companyId);
            offlinePreJobBo.setTrainingType("offline");
            offlinePreJobBo.setMeetingType("pre-job");
            offlinePreJobBo.setIsDeleted(0L);
            List<HotTrainingVo> offlinePreJobPlans = hotTrainingService.queryList(offlinePreJobBo);
            if (offlinePreJobPlans != null && !offlinePreJobPlans.isEmpty()) {
                for (HotTrainingVo t : offlinePreJobPlans) {
                    if (t == null || t.getId() == null) {
                        continue;
                    }
                    String participantIds = t.getParticipantIds();
                    if (StringUtils.isNotBlank(participantIds)) {
                        String[] userIds = participantIds.split(",");
                        offlinePreJobUserIds.addAll(Arrays.asList(userIds));
                    }
                }
            }
        }

        // 1. 覆盖率检查
        for (HotDriverVo d : drivers) {
            if (StringUtils.isBlank(d.getPhone())) continue;

            boolean isCompliant = false;
            String riskMsg = "未培训";
            String riskType = "not_trained";
            String link = null;
            String tId = null;
            String pId = null;

            if (StringUtils.isNotBlank(d.getId()) && offlinePreJobUserIds.contains(d.getId())) {
                isCompliant = true;
            }

            List<HotTrainingParticipantVo> myOnlineParts = onlinePartMap.get(d.getPhone());
            if (myOnlineParts != null && !myOnlineParts.isEmpty()) {
                for (HotTrainingParticipantVo p : myOnlineParts) {
                    boolean completed = (p.getIsCompleted() != null && p.getIsCompleted() == 1L)
                            || (p.getLearnProgress() != null && p.getLearnProgress().compareTo(BigDecimal.valueOf(100)) >= 0);
                    if (completed && p.getCompleteTime() != null) {
                        isCompliant = true;
                        break;
                    }
                    if (riskType.equals("not_trained")) {
                        riskMsg = "未完成";
                        riskType = "not_completed";
                        link = "/securityManagement/training";
                        tId = String.valueOf(p.getTrainingId());
                        pId = String.valueOf(p.getId());
                    }
                }
            }

            if (!isCompliant) {
                addRisk(risks, "岗前培训: 驾驶员 (" + d.getName() + ") " + riskMsg, riskType, "岗前培训", "pre_job", 1, d.getId(), tId, link, tId, pId);
            }
        }

        cat.setItems(risks);
        cat.setCount(risks.size());
        categories.add(cat);
    }

    private String getTrainingTypeLabel(String type) {
        if ("pre-job".equals(type)) return "岗前培训";
        if ("daily".equals(type)) return "日常培训";
        // if ("three_level".equals(type)) return "三级教育";
        if ("accident".equals(type)) return "事故培训";
        if ("special".equals(type)) return "专题培训";
        if ("violation".equals(type)) return "违章培训";
        if ("emergency".equals(type)) return "应急培训";
        if ("prevention".equals(type)) return "双重预防学习";
        if ("education".equals(type)) return "再教育培训";
        if ("offline".equals(type)) return "线下培训";
        return "其他培训";
    }

    private void checkDateRisk(List<RiskItemVo> list, LocalDate date, String titlePrefix, String moduleName, String moduleType, long warningDays, Integer personnelType, String personnelId, Object businessId, String link) {
        checkDateRisk(list, date, titlePrefix, moduleName, moduleType, warningDays, personnelType, personnelId, businessId != null ? String.valueOf(businessId) : null, link);
    }

    private void checkDateRisk(List<RiskItemVo> list, LocalDate date, String titlePrefix, String moduleName, String moduleType, long warningDays, Integer personnelType, String personnelId, String businessId, String link) {
        checkDateRisk(list, date, titlePrefix, moduleName, moduleType, warningDays, personnelType, personnelId, businessId, link, null, null);
    }

    private void checkDateRisk(List<RiskItemVo> list, LocalDate date, String titlePrefix, String moduleName, String moduleType, long warningDays, Integer personnelType, String personnelId, String businessId, String link, String trainingId, String participantId) {
        if (date == null) {
            addRisk(list, titlePrefix + "有效期为空", "expired", moduleName, moduleType, personnelType, personnelId, businessId, null, link, trainingId, participantId);
            return;
        }
        LocalDate now = LocalDate.now();
        long days = ChronoUnit.DAYS.between(now, date);

        if (days < 0) {
            addRisk(list, titlePrefix + " 已过期 (" + Math.abs(days) + ") 天", "expired", moduleName, moduleType, personnelType, personnelId, businessId, null, link, trainingId, participantId);
        } else if (days <= warningDays) {
            addRisk(list, titlePrefix + " 剩余 (" + days + ") 天到期", "warning", moduleName, moduleType, personnelType, personnelId, businessId, null, link, trainingId, participantId);
        }
    }

    private void addRisk(List<RiskItemVo> list, String title, String type, String moduleName, String moduleType, String link) {
        addRisk(list, title, type, moduleName, moduleType, null, null, null, null, link, null, null);
    }

    private void addRisk(List<RiskItemVo> list, String title, String type, String moduleName, String moduleType, String configKey, String link) {
        addRisk(list, title, type, moduleName, moduleType, null, null, null, configKey, link, null, null);
    }

    private void addRisk(List<RiskItemVo> list, String title, String type, String moduleName, String moduleType, Integer personnelType, String personnelId, String link) {
        addRisk(list, title, type, moduleName, moduleType, personnelType, personnelId, null, null, link, null, null);
    }

    private void addRisk(List<RiskItemVo> list, String title, String type, String moduleName, String moduleType, Integer personnelType, String personnelId, String businessId, String link) {
        addRisk(list, title, type, moduleName, moduleType, personnelType, personnelId, businessId, null, link, null, null);
    }

    private void addRisk(List<RiskItemVo> list, String title, String type, String moduleName, String moduleType, Integer personnelType, String personnelId, String businessId, String link, String trainingId, String participantId) {
        addRisk(list, title, type, moduleName, moduleType, personnelType, personnelId, businessId, null, link, trainingId, participantId);
    }

    private void addRisk(List<RiskItemVo> list, String title, String type, String moduleName, String moduleType, Integer personnelType, String personnelId, String businessId, String configKey, String link, String trainingId, String participantId) {
        RiskItemVo item = new RiskItemVo();
        item.setTitle(title);
        item.setType(type);
        item.setModuleName(moduleName);
        item.setModuleType(moduleType);
        item.setPersonnelType(personnelType);
        item.setPersonnelId(personnelId);
        item.setBusinessId(businessId);
        item.setConfigKey(configKey);
        item.setLink(link);
        item.setTrainingId(trainingId);
        item.setParticipantId(participantId);
        list.add(item);
    }

    private LocalDate convertDateToLocalDate(Date date) {
        if (date == null) return null;
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    }

    private LocalDate parseYearMonth(String yearMonth) {
        try {
            // 假设格式为 "yyyy-MM"
            if (yearMonth.length() == 7) {
                return LocalDate.parse(yearMonth + "-01");
            }
        } catch (Exception e) {
            // 忽略
        }
        return null;
    }

    private LocalDate parseYearMonthEnd(String yearMonth) {
        if (StringUtils.isBlank(yearMonth)) {
            return null;
        }
        LocalDate date = parseYearMonth(yearMonth);
        if (date == null) {
            return null;
        }
        return date.plusMonths(1).minusDays(1);
    }
}
