package com.bonus.material.settlement.service.impl;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.bonus.common.biz.constant.GlobalConstants;
import com.bonus.common.biz.domain.ProjectTreeBuild;
import com.bonus.common.biz.domain.ProjectTreeNode;
import com.bonus.common.biz.utils.RequestContext;
import com.bonus.common.core.exception.ServiceException;
import com.bonus.common.core.utils.DateUtils;
import com.bonus.common.core.utils.StringUtils;
import com.bonus.common.core.web.domain.AjaxResult;
import com.bonus.common.security.utils.SecurityUtils;
import com.bonus.material.basic.domain.BmAgreementInfo;
import com.bonus.material.basic.domain.BmConfig;
import com.bonus.material.basic.domain.BmProject;
import com.bonus.material.basic.mapper.BmAgreementInfoMapper;
import com.bonus.material.basic.mapper.BmConfigMapper;
import com.bonus.material.common.domain.dto.SelectDto;
import com.bonus.material.common.domain.vo.AgreementVo;
import com.bonus.material.settlement.domain.SltAgreementApply;
import com.bonus.material.settlement.domain.SltAgreementReduce;
import com.bonus.material.settlement.domain.SltAgreementRelation;
import com.bonus.material.settlement.domain.vo.SltInfoVo;
import com.bonus.material.settlement.mapper.SltAgreementReduceMapper;
import com.bonus.material.task.domain.TmTask;
import com.bonus.material.task.mapper.TmTaskMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.bonus.material.settlement.mapper.SltAgreementInfoMapper;
import com.bonus.material.settlement.domain.SltAgreementInfo;
import com.bonus.material.settlement.domain.dto.PeriodCostQueryDto;
import com.bonus.material.settlement.domain.vo.PeriodCostResultVo;
import com.bonus.material.settlement.domain.vo.PeriodCostSummaryVo;
import com.bonus.material.settlement.service.ISltAgreementInfoService;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Map;
import java.util.Objects;

/**
 * 结算信息Service业务层处理
 *
 * @author xsheng
 * @date 2024-10-16
 */
@Slf4j
@Service
public class SltAgreementInfoServiceImpl implements ISltAgreementInfoService {

    // 业务人员配置 -- 工器具结算权限账户bm_config表item_name键值
    private static final String EQUIPMENT_COST_USERS = "equipment_cost_users";

    // 业务人员配置 -- 安全工器具结算权限账户bm_config表item_name键值
    private static final String SAFETY_EQUIPMENT_COST_USERS = "safety_equipment_cost_users";

    // 工器具结算类型settlement_type键值
    private static final int EQUIPMENT_SETTLEMENT_TYPE =  GlobalConstants.INT_1;

    // 安全工器具结算类型settlement_type键值
    private static final int SAFETY_EQUIPMENT_SETTLEMENT_TYPE =  GlobalConstants.INT_2;

    @Autowired
    private SltAgreementInfoMapper sltAgreementInfoMapper;

    @Autowired
    private BmAgreementInfoMapper bmAgreementInfoMapper;

    @Autowired
    private BmConfigMapper bmConfigMapper;

    @Autowired
    private TmTaskMapper taskMapper;

    @Resource
    private SltAgreementReduceMapper sltAgreementRecudceMapper;

    // 定义线程级缓存
    private static final ThreadLocal<List<SltAgreementInfo>> leaseListCache = new ThreadLocal<>();
    // 丢失结算列表缓存
    private static final ThreadLocal<List<SltAgreementInfo>> lostListCache = new ThreadLocal<>();
    // 费用减免列表缓存
    private static final ThreadLocal<List<SltAgreementReduce>> reductionListCache = new ThreadLocal<>();
    // 维修结算列表缓存
    private static final ThreadLocal<List<SltAgreementInfo>> repairListCache = new ThreadLocal<>();
    // 报废结算列表缓存
    private static final ThreadLocal<List<SltAgreementInfo>> scrapListCache = new ThreadLocal<>();

    /**
     * 查询结算信息
     *
     * @param id 结算信息主键
     * @return 结算信息
     */
    @Override
    public SltAgreementInfo selectSltAgreementInfoById(Long id) {
        return sltAgreementInfoMapper.selectSltAgreementInfoById(id);
    }

    // 暴露 public 方法给外层清理缓存、防止内存泄漏
    @Override
    public void clearCache() {
        leaseListCache.remove();
        lostListCache.remove();
        reductionListCache.remove();
        repairListCache.remove();
        scrapListCache.remove();
    }

    /**
     * 查询结算信息列表
     *
     * @param sltAgreementInfo 结算信息
     * @return 结算信息
     */
    @Override
    public List<SltAgreementInfo> selectSltAgreementInfoList(SltAgreementInfo sltAgreementInfo) {
        return sltAgreementInfoMapper.selectSltAgreementInfoList(sltAgreementInfo);
    }

    @Override
    public BigDecimal sum(BigDecimal... values) {
        BigDecimal total = BigDecimal.ZERO;
        for (BigDecimal v : values) {
            if (v != null) {
                total = total.add(v);
            }
        }
        return total;
    }

    @Override
    public List<SltAgreementInfo> getSltAgreementInfo4Project(SltAgreementInfo bean) {
        Long userId = SecurityUtils.getLoginUser().getUserid();
        List<SltAgreementInfo> list = sltAgreementInfoMapper.getSltAgreementInfo4Project(bean);

        // 删除 null 对象
        list.removeIf(Objects::isNull);
        list.removeIf(info -> info.getSltStatus() != null && info.getSltStatus().equals("2"));
        // 遍历列表，设置默认值
        list.forEach(obj -> {
            if (obj.getIsSlt() == null) { obj.setIsSlt("0");}
        });

        return list;
    }

    @Override
    public SltInfoVo getSltInfo(SltAgreementInfo info) {
        SltInfoVo sltInfoVo = new SltInfoVo();
        sltInfoVo.setUnitName(info.getUnitName());
        sltInfoVo.setProjectName(info.getProjectName());
        BigDecimal leaseCost = BigDecimal.ZERO;
        BigDecimal repairCost = BigDecimal.ZERO;
        BigDecimal scrapCost = BigDecimal.ZERO;
        BigDecimal loseCost = BigDecimal.ZERO;
        BigDecimal reducCost = BigDecimal.ZERO;
        //租赁费用列表
        List<SltAgreementInfo> leaseList = getLeaseList(info);
        //维修费用列表
        List<SltAgreementInfo> repairList = getRepairList(info);
        //报废费用列表
        List<SltAgreementInfo> scrapList = getScrapList(info);
        //丢失费用列表
        List<SltAgreementInfo> loseList = getLoseList(info);
        //费用减免列表
        List<SltAgreementReduce> reductionList = getReductionList(info);

        if (CollectionUtils.isEmpty(leaseList) && CollectionUtils.isEmpty(repairList)
            && CollectionUtils.isEmpty(scrapList) && CollectionUtils.isEmpty(loseList)
            && CollectionUtils.isEmpty(reductionList))
        {
            return null;
        }


        sltInfoVo.setLeaseList(leaseList);
        sltInfoVo.setRepairList(repairList);
        sltInfoVo.setScrapList(scrapList);
        sltInfoVo.setLoseList(loseList);
        sltInfoVo.setReductionList(reductionList);
        for (SltAgreementInfo lease : leaseList) {
            if (lease.getCosts() != null) {
                leaseCost = leaseCost.add(lease.getCosts());
            }
        }
        for (SltAgreementInfo repair : repairList) {
            if(repair.getCosts() != null && (repair.getPartType().equals("收费"))) {
                repairCost = repairCost.add(repair.getCosts());
            }
        }
        for (SltAgreementInfo scrap : scrapList) {
            if(scrap.getCosts() != null && (scrap.getPartType().equals("收费"))) {
                scrapCost = scrapCost.add(scrap.getCosts());
            }
        }
        for (SltAgreementInfo lose : loseList) {
            if(lose.getCosts()!=null){
                loseCost = loseCost.add(lose.getCosts());
            }
        }
        for (SltAgreementReduce reduction : reductionList) {
            if(reduction.getLeaseMoney()!=null){
                reducCost = reducCost.add(reduction.getLeaseMoney());
            }
        }
        sltInfoVo.setLeaseCost(leaseCost);
        sltInfoVo.setRepairCost(repairCost);
        sltInfoVo.setScrapCost(scrapCost);
        sltInfoVo.setLoseCost(loseCost);
        sltInfoVo.setReductionCost(reducCost);
        List<SltAgreementRelation> relations = getRelations(leaseList, repairList, scrapList, loseList, info);
        sltInfoVo.setRelations(relations);
        return sltInfoVo;
    }

    /**
     * 批量查询结算信息列表 -- 未结算报表专用
     */
    @Override
    public SltInfoVo getSltInfoReportBatch(SltAgreementInfo info) {
        SltInfoVo sltInfoVo = new SltInfoVo();
        sltInfoVo.setUnitName(info.getUnitName());
        sltInfoVo.setProjectName(info.getProjectName());
        BigDecimal leaseCost = BigDecimal.ZERO, repairCost= BigDecimal.ZERO ;
        BigDecimal scrapCost= BigDecimal.ZERO, loseCost= BigDecimal.ZERO, reducCost= BigDecimal.ZERO;

        if (leaseListCache.get() == null) {
            // 调用一次性批量租赁费用查询
            leaseListCache.set(sltAgreementInfoMapper.getLeaseListBatch(info));
        }
        if (lostListCache.get() == null) {
            // 调用一次性批量丢失费用查询
            lostListCache.set(sltAgreementInfoMapper.getLoseListBatch(info));
        }
        if (reductionListCache.get() == null) {
            // 调用一次性批量减免费用查询
            SltAgreementReduce bean =new SltAgreementReduce();
            bean.setAgreementIds(info.getAgreementIds());
            reductionListCache.set(sltAgreementRecudceMapper.getReductionListBatch(bean));
        }
        if (repairListCache.get() == null) {
            // 调用一次性批量维修费用查询
            repairListCache.set(sltAgreementInfoMapper.getRepairDetailsListBatch(info, null));
        }
        if (scrapListCache.get() == null) {
            // 调用一次性批量报废费用查询
            scrapListCache.set(sltAgreementInfoMapper.getScrapDetailsListBatch(info));
        }
        // 查询租赁费用列表
        List<SltAgreementInfo> leaseList = getLeaseBtachList(info);
        //维修费用列表
        List<SltAgreementInfo> repairList = getRepairListBatch(info);
        //报废费用列表
        List<SltAgreementInfo> scrapList = getScrapListBatch(info);
        //丢失费用列表
        List<SltAgreementInfo> loseList = getLoseListBatch(info);
        //费用减免列表
        List<SltAgreementReduce> reductionList = getReductionListBatch(info);

        // 删除掉各项费用都为空的数据
//        if (CollectionUtils.isEmpty(leaseList) && CollectionUtils.isEmpty(repairList)
//                && CollectionUtils.isEmpty(scrapList) && CollectionUtils.isEmpty(loseList)
//                && CollectionUtils.isEmpty(reductionList))
//        {
//            return null;
//        }

        sltInfoVo.setLeaseList(leaseList);
        sltInfoVo.setRepairList(repairList);
        sltInfoVo.setScrapList(scrapList);
        sltInfoVo.setLoseList(loseList);
        sltInfoVo.setReductionList(reductionList);
        for (SltAgreementInfo lease : leaseList) {
            if (lease.getCosts() != null) {
                leaseCost = leaseCost.add(lease.getCosts());
            }
        }
        for (SltAgreementInfo repair : repairList) {
            if (repair.getCosts() != null && (repair.getPartType().equals("收费"))) {
                repairCost = repairCost.add(repair.getCosts());
            }
        }
        for (SltAgreementInfo scrap : scrapList) {
            if (scrap.getCosts() != null && (scrap.getPartType().equals("收费"))) {
                scrapCost = scrapCost.add(scrap.getCosts());
            }
        }
        for (SltAgreementInfo lose : loseList) {
            if (lose.getCosts() != null) {
                loseCost = loseCost.add(lose.getCosts());
            }
        }
        for (SltAgreementReduce reduction : reductionList) {
            if (reduction.getLeaseMoney() != null) {
                reducCost = reducCost.add(reduction.getLeaseMoney());
            }
        }
        sltInfoVo.setLeaseCost(leaseCost);
        sltInfoVo.setRepairCost(repairCost);
        sltInfoVo.setScrapCost(scrapCost);
        sltInfoVo.setLoseCost(loseCost);
        sltInfoVo.setReductionCost(reducCost);
        List<SltAgreementRelation> relations = getRelations(leaseList, repairList, scrapList, loseList, info);
        sltInfoVo.setRelations(relations);
        return sltInfoVo;
    }

    @Override
    public List<SltAgreementApply> getSltExam(SltAgreementInfo bean) {
        return sltAgreementInfoMapper.getSltExam(bean);
    }

    /**
     * 批量分批查询工具
     *
     * @param ids       待查询的id集合
     * @param batchSize 每次查询的最大数量
     * @param queryFunc 查询函数，比如 service::queryByIds
     * @return 合并后的结果集合
     * @param <T> id类型
     * @param <R> 返回结果类型
     */
    public static <T, R> List<R> batchQuery(List<T> ids, int batchSize, Function<List<T>, List<R>> queryFunc) {
        if (ids == null || ids.isEmpty()) {
            return Collections.emptyList();
        }

        List<R> result = new ArrayList<>();
        int total = ids.size();
        for (int i = 0; i < total; i += batchSize) {
            int end = Math.min(i + batchSize, total);
            List<T> subList = ids.subList(i, end);
            List<R> batchResult = queryFunc.apply(subList);
            if (batchResult != null && !batchResult.isEmpty()) {
                result.addAll(batchResult);
            }
        }
        return result;
    }

    /**
     * 新增结算信息
     *
     * @param sltAgreementInfo 结算信息
     * @return 结果
     */
    @Override
    public int insertSltAgreementInfo(SltAgreementInfo sltAgreementInfo) {
        sltAgreementInfo.setCreateTime(DateUtils.getNowDate());
        try {
            return sltAgreementInfoMapper.insertSltAgreementInfo(sltAgreementInfo);
        } catch (Exception e) {
            throw new ServiceException("错误信息描述");
        }
    }

    /**
     * 修改结算信息
     *
     * @param sltAgreementInfo 结算信息
     * @return 结果
     */
    @Override
    public int updateSltAgreementInfo(SltAgreementInfo sltAgreementInfo) {
        sltAgreementInfo.setUpdateTime(DateUtils.getNowDate());
        try {
            return sltAgreementInfoMapper.updateSltAgreementInfo(sltAgreementInfo);
        } catch (Exception e) {
            throw new ServiceException("错误信息描述");
        }
    }

    /**
     * 批量删除结算信息
     *
     * @param ids 需要删除的结算信息主键
     * @return 结果
     */
    @Override
    public int deleteSltAgreementInfoByIds(Long[] ids) {
        return sltAgreementInfoMapper.deleteSltAgreementInfoByIds(ids);
    }

    /**
     * 删除结算信息信息
     *
     * @param id 结算信息主键
     * @return 结果
     */
    @Override
    public int deleteSltAgreementInfoById(Long id) {
        return sltAgreementInfoMapper.deleteSltAgreementInfoById(id);
    }

    /**
     * 获取单个协议的领料明细
     */
    public List<SltAgreementInfo> getLeaseList(SltAgreementInfo info) {
        List<SltAgreementInfo> oneOfList = sltAgreementInfoMapper.getLeaseList(info);
        List<SltAgreementInfo> leaseList = new ArrayList<>(oneOfList);

        for (SltAgreementInfo bean : leaseList) {
            if (null == bean.getLeasePrice() || bean.getLeasePrice().compareTo(BigDecimal.ZERO) < 0) {
                bean.setLeasePrice(BigDecimal.ZERO);
            } else {
                bean.setLeasePrice(bean.getLeasePrice().setScale(3, RoundingMode.DOWN));
            }
            if (null == bean.getNum() || bean.getNum().compareTo(BigDecimal.ZERO) < 0) {
                bean.setNum(BigDecimal.ZERO);
            }
            if (null == bean.getLeaseDays()) {
                bean.setLeaseDay(0L);
            }
            BigDecimal leasePrice = bean.getLeasePrice();
            BigDecimal num = bean.getNum();
            BigDecimal leaseDays = new BigDecimal(bean.getLeaseDays());
            if (leaseDays.compareTo(BigDecimal.ZERO) < 0) {
                leaseDays = BigDecimal.ZERO;
            }
            BigDecimal costs = leasePrice.multiply(num).multiply(leaseDays).setScale(GlobalConstants.INT_2, RoundingMode.DOWN);
            bean.setCosts(costs);
        }
        return leaseList;
    }

    /**
     * 获取多个协议的领料明细
     */
    public List<SltAgreementInfo> getLeaseBtachList(SltAgreementInfo info) {
        // 领料就不要每次都执行SQL
        List<SltAgreementInfo> leaseList = leaseListCache.get()
                .stream()
                .filter(bean -> bean.getAgreementId().equals(info.getAgreementId())).collect(Collectors.toList());

        for (SltAgreementInfo bean : leaseList) {
            // 同步过来可能有负数，如果是负数就改成0
            if (null == bean.getLeasePrice() || bean.getLeasePrice().compareTo(BigDecimal.ZERO) < 0) {
                bean.setLeasePrice(BigDecimal.ZERO);
            } else {
                bean.setLeasePrice(bean.getLeasePrice().setScale(3, RoundingMode.DOWN));
            }
            if (null == bean.getNum() || bean.getNum().compareTo(BigDecimal.ZERO) < 0) {
                bean.setNum(BigDecimal.ZERO);
            }
            if (null == bean.getLeaseDays()) {
                bean.setLeaseDay(0L);
            }
            BigDecimal leasePrice = bean.getLeasePrice();
            BigDecimal num = bean.getNum();
            BigDecimal leaseDays = new BigDecimal(bean.getLeaseDays());
            if (leaseDays.compareTo(BigDecimal.ZERO) < 0) {
                leaseDays = BigDecimal.ZERO;
            }
            BigDecimal costs = leasePrice.multiply(num).multiply(leaseDays).setScale(GlobalConstants.INT_2, RoundingMode.DOWN);
            bean.setCosts(costs);
        }
        return leaseList;
    }

    public List<SltAgreementInfo> getRepairList(SltAgreementInfo info) {
        List<SltAgreementInfo> repairList = new ArrayList<>();
        List<TmTask> taskList = taskMapper.getTaskIdList(info);
        if (null != taskList && !taskList.isEmpty()) {
            List<SltAgreementInfo> repairDetailsList = sltAgreementInfoMapper.getRepairDetailsList(info, taskList);
            repairList.addAll(repairDetailsList);
        }
        return repairList;
    }

    public List<SltAgreementInfo> getRepairListBatch(SltAgreementInfo info) {
        return repairListCache.get()
                .stream()
                .filter(bean -> bean.getAgreementId().equals(info.getAgreementId()))
                .collect(Collectors.toList());
    }

    public List<SltAgreementInfo> getScrapList(SltAgreementInfo info) {
        List<SltAgreementInfo> scrapList = new ArrayList<>();
        List<TmTask> taskList = taskMapper.getTaskIdList(info);
        if (null != taskList && !taskList.isEmpty()) {
            List<SltAgreementInfo> scrapDetailsList = sltAgreementInfoMapper.getScrapDetailsList(info, taskList);
            scrapList.addAll(scrapDetailsList);
        }
        return scrapList;
    }

    /**
     * 获取多个协议的报废明细
     */
    public List<SltAgreementInfo> getScrapListBatch(SltAgreementInfo info) {
        return scrapListCache.get()
                .stream()
                .filter(bean -> bean.getAgreementId().equals(info.getAgreementId()))
                .collect(Collectors.toList());
    }

    public List<SltAgreementInfo> getLoseList(SltAgreementInfo info) {
        List<SltAgreementInfo> oneOfList = sltAgreementInfoMapper.getLoseList(info);
        List<SltAgreementInfo> loseList = new ArrayList<>(oneOfList);

        for (SltAgreementInfo bean : loseList) {
            if (null == bean.getBuyPrice()) {
                bean.setBuyPrice(BigDecimal.ZERO);
            }
            if (null == bean.getNum()) {
                bean.setNum(BigDecimal.ZERO);
            }
            BigDecimal buyPrice = bean.getBuyPrice();
            BigDecimal num = bean.getNum();
            // 原价 x 数量
            BigDecimal costs = buyPrice.multiply(num);
            //计算租赁费用
            bean.setCosts(costs);
        }
        return loseList;
    }

    public List<SltAgreementInfo> getLoseListBatch(SltAgreementInfo info) {
        List<SltAgreementInfo> loseList = lostListCache.get()
                .stream()
                .filter(bean -> bean.getAgreementId().equals(info.getAgreementId()))
                .collect(Collectors.toList());

        for (SltAgreementInfo bean : loseList) {
            if (null == bean.getBuyPrice()) {
                bean.setBuyPrice(BigDecimal.ZERO);
            }
            if (null == bean.getNum()) {
                bean.setNum(BigDecimal.ZERO);
            }
            BigDecimal buyPrice = bean.getBuyPrice();
            BigDecimal num = bean.getNum();
            // 原价 x 数量
            BigDecimal costs = buyPrice.multiply(num);
            //计算租赁费用
            bean.setCosts(costs);
        }
        return loseList;
    }

    private List<SltAgreementReduce> getReductionList(SltAgreementInfo info) {
        SltAgreementReduce bean =new SltAgreementReduce();
        bean.setAgreementId(info.getAgreementId());
        return sltAgreementRecudceMapper.getReductionList(bean);
    }

    private List<SltAgreementReduce> getReductionListBatch(SltAgreementInfo info) {
        return reductionListCache.get()
                .stream()
                .filter(bean -> bean.getAgreementId().equals(info.getAgreementId()))
                .collect(Collectors.toList());
    }

    private List<SltAgreementRelation> getRelations(List<SltAgreementInfo> leaseList, List<SltAgreementInfo> repairList,
                                                    List<SltAgreementInfo> scrapList, List<SltAgreementInfo> loseList,
                                                    SltAgreementInfo sltInfo)
    {
        List<SltAgreementRelation> relations = new ArrayList<>();
        SltAgreementRelation relation = new SltAgreementRelation();
        BigDecimal loseCost = BigDecimal.ZERO;
        BigDecimal leaseCost = BigDecimal.ZERO;
        BigDecimal scrapCost = BigDecimal.ZERO;
        BigDecimal repairCost = BigDecimal.ZERO;
        for (SltAgreementInfo lease : leaseList) {
            if (lease.getAgreementId().equals(sltInfo.getAgreementId())) {
                relation.setAgreementId(String.valueOf(lease.getAgreementId()));
                relation.setProjectName(lease.getProjectName());
                relation.setUnitName(lease.getUnitName());
                relation.setCompanyId(lease.getCompanyId());
                BigDecimal cost = lease.getCosts();
                leaseCost = leaseCost.add(cost);
            }
        }
        for (SltAgreementInfo repair : repairList) {
            if (repair.getAgreementId().equals(sltInfo.getAgreementId())) {
                BigDecimal cost = repair.getCosts();
                repairCost = repairCost.add(cost);
            }
        }
        for (SltAgreementInfo scrap : scrapList) {
            if (scrap.getAgreementId().equals(sltInfo.getAgreementId())) {
                BigDecimal cost = scrap.getCosts();
                scrapCost = scrapCost.add(cost);
            }
        }
        for (SltAgreementInfo lose : loseList) {
            if (lose.getAgreementId().equals(sltInfo.getAgreementId())) {
                relation.setAgreementId(String.valueOf(lose.getAgreementId()));
                relation.setProjectName(lose.getProjectName());
                relation.setUnitName(lose.getUnitName());
                relation.setCompanyId(lose.getCompanyId());
                BigDecimal cost = lose.getCosts();
                loseCost = loseCost.add(cost);
            }
        }
        relation.setLeaseCost(leaseCost);
        relation.setRepairCost(repairCost);
        relation.setScrapCost(scrapCost);
        relation.setLoseCost(loseCost);
        relations.add(relation);
        return relations;
    }

    /**
     * 提交结算信息
     *
     * @param sltInfoVo 结算信息
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int submitCosts(@NotNull(message = "结算信息不能为空") SltInfoVo sltInfoVo) {
        // 检查登录用户是否有结算权限
        int loginUserSettlementTypePermission = this.checkLoginUserHasSettlementPermission();
        sltInfoVo.setSettlementType(loginUserSettlementTypePermission);
        if (sltInfoVo.getSettlementType() == null) {
            throw new ServiceException("结算类型错误，结算权限码为空");
        }
        if (sltInfoVo.getSettlementType()!=EQUIPMENT_SETTLEMENT_TYPE && sltInfoVo.getSettlementType()!=SAFETY_EQUIPMENT_SETTLEMENT_TYPE) {
            throw new ServiceException("结算类型错误，结算权限码不合法！");
        }
        try {
            if (sltInfoVo.getAgreementIds() != null) {
                for (Long agreementId : sltInfoVo.getAgreementIds()) {
                    if (agreementId == null || agreementId <= 0) {
                        throw new ServiceException("协议id错误");
                    }



                    int theSettledCount = sltAgreementInfoMapper.selectTheSettledCountByAgreementIdType(sltInfoVo.getSettlementType(), agreementId);
                    if (sltInfoVo.getAgreementIds().length <= 1) {
                        // 如果不是批量提交，已结算的费用类型就直接提示已结算
                        if (theSettledCount > 0) {
                            String costType = Objects.equals(sltInfoVo.getSettlementType(), EQUIPMENT_SETTLEMENT_TYPE) ? "工器具" : "安全工器具";
                            throw new ServiceException("协议:" + agreementId + "的" + costType + "费用已结算");
                        }
                    } else {
                        // 如果是批量提交，一次性提交多个，那么已结算的就忽略跳过
                        if (theSettledCount > 0) {
                            continue;
                        }
                    }
                    Long id;
                    sltInfoVo.setAgreementId(agreementId);
                    // 计算总成本
                    BigDecimal totalCost = BigDecimal.ZERO;
                    SltAgreementInfo info = new SltAgreementInfo();
                    info.setAgreementId(agreementId);

                    //获取该协议下的所有维修任务
                    List<TmTask> repairList = taskMapper.getTaskIdByAgreementId(info);
                    if(repairList != null && !repairList.isEmpty()){
                        throw new ServiceException("结算中存在维修和报废未完成的任务，无法结算！");
                    }

                    // 获取各项成本并累加
                    totalCost = totalCost.add(getLeaseList(info).stream()
                            .map(SltAgreementInfo::getCosts)
                            .filter(Objects::nonNull)
                            .reduce(BigDecimal.ZERO, BigDecimal::add));

                    totalCost = totalCost.add(getRepairList(info).stream()
                            .map(SltAgreementInfo::getCosts)
                            .filter(Objects::nonNull)
                            .reduce(BigDecimal.ZERO, BigDecimal::add));

                    totalCost = totalCost.add(getScrapList(info).stream()
                            .map(SltAgreementInfo::getCosts)
                            .filter(Objects::nonNull)
                            .reduce(BigDecimal.ZERO, BigDecimal::add));

                    totalCost = totalCost.add(getLoseList(info).stream()
                            .map(SltAgreementInfo::getCosts)
                            .filter(Objects::nonNull)
                            .reduce(BigDecimal.ZERO, BigDecimal::add));

                    totalCost = totalCost.subtract(getReductionList(info).stream()
                            .map(SltAgreementReduce::getLeaseMoney)
                            .filter(Objects::nonNull)
                            .reduce(BigDecimal.ZERO, BigDecimal::add));

                    // 设置总成本
                    sltInfoVo.setTotalCostAll(totalCost);
                    //查找是否驳回
                    int countReject = sltAgreementInfoMapper.getRejectCount(sltInfoVo);
                    // 判断已经结算的有几个
                    int beforeCount = sltAgreementInfoMapper.getBeforeSettlementCount(sltInfoVo);
                    // 更新结算协议状态，0未结算，1全部结算 2部分结算
                    int updateAgreementCount = bmAgreementInfoMapper.updateBmAgreementInfo(new BmAgreementInfo().setAgreementId(agreementId).setIsSlt(beforeCount == 0 ? 2L : 1L));
                    if (updateAgreementCount == 0) {
                        throw new ServiceException("bm_agreement_info修改失败");
                    }
                    if (countReject > 0) {
                        id = sltAgreementInfoMapper.getRejectId(sltInfoVo);
                        //修改
                        int countAgain = sltAgreementInfoMapper.updateRejectCount(sltInfoVo);
                        if (countAgain == 0) {
                            throw new ServiceException("slt_agreement_apply修改失败");
                        }
                        sltInfoVo.setUpdateTime(DateUtils.getNowDate());
                        sltInfoVo.setCreateBy(String.valueOf(SecurityUtils.getLoginUser().getUserid()));
                    } else {
                        sltInfoVo.setCreateTime(DateUtils.getNowDate());
                        sltInfoVo.setCreateBy(String.valueOf(SecurityUtils.getLoginUser().getUserid()));
                        //新增
                        int countOne = sltAgreementInfoMapper.insertSltAgreementApply(sltInfoVo);
                        if (countOne != 1) {
                            throw new ServiceException("slt_agreement_apply新增失败");
                        }
                        // 插入成功后，sltInfoVo 的 id 属性将被自动设置为新生成的主键值
                        id = sltInfoVo.getId();
                        sltInfoVo.setUpdateTime(DateUtils.getNowDate());
                    }
                    if (!sltInfoVo.getLeaseList().isEmpty()) {
                        List<SltAgreementInfo> filteredLeaseList = sltInfoVo.getLeaseList().stream()
                                .filter(lease -> lease.getAgreementId().equals(sltInfoVo.getAgreementId()))
                                .collect(Collectors.toList());
                        if (!filteredLeaseList.isEmpty()) {
                            sltAgreementInfoMapper.insertSltAgreementDetailLease(filteredLeaseList, id);
                        }
                    }
                    if (!sltInfoVo.getRepairList().isEmpty()) {
                        List<SltAgreementInfo> filteredRepairList = sltInfoVo.getRepairList().stream()
                                .filter(lease -> lease.getAgreementId().equals(sltInfoVo.getAgreementId()))
                                .collect(Collectors.toList());
                        if (!filteredRepairList.isEmpty()) {
                            sltAgreementInfoMapper.insertSltAgreementDetailRepair(filteredRepairList, id);
                        }
                    }
                    if (!sltInfoVo.getScrapList().isEmpty()) {
                        List<SltAgreementInfo> filteredScrapList = sltInfoVo.getScrapList().stream()
                                .filter(lease -> lease.getAgreementId().equals(sltInfoVo.getAgreementId()))
                                .collect(Collectors.toList());
                        if (!filteredScrapList.isEmpty()) {
                            sltAgreementInfoMapper.insertSltAgreementDetailScrap(filteredScrapList, id);
                        }
                    }
                    if (!sltInfoVo.getLoseList().isEmpty()) {
                        List<SltAgreementInfo> filteredLoseList = sltInfoVo.getLoseList().stream()
                                .filter(lease -> lease.getAgreementId().equals(sltInfoVo.getAgreementId()))
                                .collect(Collectors.toList());
                        if (!filteredLoseList.isEmpty()) {
                            sltAgreementInfoMapper.insertSltAgreementDetailLose(filteredLoseList, id);
                        }
                    }
                }
            }
            return GlobalConstants.INT_1;
        } catch (Exception e) {
            throw new ServiceException("错误:" + e.getMessage());
        }
    }

    /**
     * 判断当前登陆用户是否有结算权限，如果有则进行赋值
     * @return 结算类型值
     */
    @Override
    public int checkLoginUserHasSettlementPermission() {
        // 查询当前登陆用户信息
        Long theLoginUserId = SecurityUtils.getLoginUser().getSysUser().getUserId();

        // 去业务配置表查询当前登陆用户是否有权限
        BmConfig equipmentConfigUsers = bmConfigMapper.selectBmConfigByItemName(EQUIPMENT_COST_USERS);
        BmConfig safetyEquipmentConfigUsers = bmConfigMapper.selectBmConfigByItemName(SAFETY_EQUIPMENT_COST_USERS);
        if ((Objects.isNull(equipmentConfigUsers) || equipmentConfigUsers.getItemValue() == null)
                && (Objects.isNull(safetyEquipmentConfigUsers) || safetyEquipmentConfigUsers.getItemValue() == null))
        {
            throw new ServiceException("结算业务人员未配置，无法进行系统结算!");
        }
        if (Objects.nonNull(equipmentConfigUsers) && equipmentConfigUsers.getItemValue() != null) {
            boolean isContains = containsExactValue(equipmentConfigUsers.getItemValue(), String.valueOf(theLoginUserId));
            if (isContains) { return GlobalConstants.INT_1; }
        }
        if (Objects.nonNull(safetyEquipmentConfigUsers) && Objects.nonNull(safetyEquipmentConfigUsers.getItemValue())) {
            boolean isContains = containsExactValue(safetyEquipmentConfigUsers.getItemValue(), String.valueOf(theLoginUserId));
            if (isContains) { return GlobalConstants.INT_2; }
        }
        throw new ServiceException("没有权限进行系统结算！");
    }

    /**
     * 结算审批列表
     *
     * @param bean 结算信息
     * @return 结果
     */
    @Override
    public List<SltAgreementInfo> getSltList(SltAgreementInfo bean) {
        List<SltAgreementInfo> list = sltAgreementInfoMapper.getSltList(bean);
        if (CollectionUtils.isNotEmpty(list)) {
            for (SltAgreementInfo row : list){
                SltInfoVo sltInfoVo = null;
                sltInfoVo = getSltInfo(row);
                if (sltInfoVo != null) {
                    row.setCosts(sum(sltInfoVo.getLeaseCost(), sltInfoVo.getRepairCost(), sltInfoVo.getScrapCost(), sltInfoVo.getLoseCost()));
                }
            }
        }

        return list;
    }

    /**
     * 查询已结算报表list
     */
    @Override
    public List<SltAgreementInfo> getSltReportedList(SltAgreementInfo bean) {
        List<SltAgreementInfo> sltReportedList = sltAgreementInfoMapper.getSltReportedList(bean);
        if (sltReportedList != null) {
            sltReportedList.removeIf(Objects::isNull);
            sltReportedList.forEach(sltAgreementInfo -> {
                if (sltAgreementInfo.getCosts() == null || sltAgreementInfo.getCosts().equals(BigDecimal.ZERO)) {
                    sltAgreementInfo.setCosts(
                            sltAgreementInfo.getLeaseCost()
                            .add(sltAgreementInfo.getRepairCost())
                            .add(sltAgreementInfo.getScrapCost())
                            .add(sltAgreementInfo.getLoseCost())
                    );
                }
            });
        }
        return sltReportedList;
    }

    /**
     * 查询未结算报表list
     *
     * @param bean
     * @return
     */
    @Override
    public List<SltAgreementInfo> getSltReportList(SltAgreementInfo bean) {
        try {
            // 参数预处理，避免无效查询
            if (bean != null) {
                // 清理空字符串参数，避免无效的LIKE查询
                if (StringUtils.isEmpty(bean.getAgreementCode())) {
                    bean.setAgreementCode(null);
                }
                if (StringUtils.isEmpty(bean.getUnitName())) {
                    bean.setUnitName(null);
                }
                if (StringUtils.isEmpty(bean.getProjectName())) {
                    bean.setProjectName(null);
                }
            }

            // 执行查询
            List<SltAgreementInfo> list = sltAgreementInfoMapper.getSltReportList(bean);
            int listCount = sltAgreementInfoMapper.getSltReportListCount(bean);
            RequestContext.set("unSltAgreementListCount", listCount);


            // 优化后处理逻辑
            if (list != null && !list.isEmpty()) {
                // 使用并行流处理大数据量（当数据量大于1000时）
                if (list.size() > 1000) {
                    list.parallelStream()
                        .filter(Objects::nonNull)
                        .forEach(obj -> {
                            if (obj.getIsSlt() == null) {
                                obj.setIsSlt("0");
                            }
                        });
                } else {
                    // 小数据量使用普通流
                    list.removeIf(Objects::isNull);
                    list.forEach(obj -> {
                        if (obj.getIsSlt() == null) {
                            obj.setIsSlt("0");
                        }
                    });
                }
            }

            return list != null ? list : new ArrayList<>();

        } catch (Exception e) {
            System.err.println("查询未结算报表失败:" + e.getMessage());
            throw new ServiceException("查询未结算报表失败：" + e.getMessage());
        }
    }

    /**
     * 查询丢失报表
     */
    @Override
    public List<SltAgreementInfo> getLostReportList(SltAgreementInfo info) {
        List<SltAgreementInfo> loseList = sltAgreementInfoMapper.getLostReportList(info);

        loseList.removeIf(Objects::isNull);

        for (SltAgreementInfo bean : loseList) {
            if (null == bean.getBuyPrice()) {
                bean.setBuyPrice(BigDecimal.ZERO);
            }
            if (null == bean.getNum()) {
                bean.setNum(BigDecimal.ZERO);
            }
        }
        return loseList;
    }

    /**
     * 维修报表list
     *
     * @param bean
     */
    @Override
    public List<SltAgreementInfo> getRepairReportList(SltAgreementInfo bean) {
        return sltAgreementInfoMapper.getRepairDetailsListBatch(bean, null);
    }

    /**
     * 报废报表list
     *
     * @param bean
     */
    @Override
    public List<SltAgreementInfo> getScrapReportList(SltAgreementInfo bean) {
        List<SltAgreementInfo> scrapList = sltAgreementInfoMapper.getScrapDetailsListBatch(bean);
        return scrapList;
    }

    /**
     * 判断以逗号分隔的字符串是否包含指定的值(严格判断)
     * @param strings 以逗号分隔的字符串
     * @param target 需要判断的值
     * @return true 表示包含，false 表示不包含
     */
    public static boolean containsExactValue(String strings, String target) {
        if (strings == null || target == null) {
            return false;
        }
        if (strings.isEmpty() || target.isEmpty()) {
            return false;
        }
        String[] parts = strings.split(",");
        for (String part : parts) {
            if (part == null || part.isEmpty()) {
                continue;
            }
            if (part.equals(target)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 进行结算审批
     *
     * @param sltAgreementApply 进行结算审批
     * @return 结果
     */
    @Override
    public AjaxResult costExamine(SltAgreementApply sltAgreementApply) {
        try {
            Long userid = SecurityUtils.getLoginUser().getUserid();
            sltAgreementApply.setAuditor(String.valueOf(userid));
            sltAgreementApply.setAuditTime(DateUtils.getNowDate());
            sltAgreementApply.setUpdateTime(DateUtils.getNowDate());
            if (sltAgreementApply.getStatus().equals("3")) {
                // 驳回，更新slt协议审批状态
                int count = sltAgreementInfoMapper.costExame(sltAgreementApply);
                // 删除结算详情
                sltAgreementInfoMapper.costDeleteDetail(sltAgreementApply);
                if (count != 1) {
                    return AjaxResult.error("结算审批失败");
                } else {
                    // 更新协议状态
                    int beforeSettlementCount = sltAgreementInfoMapper.getBeforeSettlementCount(new SltInfoVo().setAgreementId(sltAgreementApply.getAgreementId()));
                    // 根据待审核+已审核的数量来决定更新到哪种状态
                    sltAgreementApply.setIsSlt(beforeSettlementCount > GlobalConstants.INT_0 ? GlobalConstants.INT_2 : GlobalConstants.INT_0);
                    int countTwo = sltAgreementInfoMapper.updateBmAgreementReject(sltAgreementApply);
                    if(countTwo != GlobalConstants.INT_1) {
                        return AjaxResult.error("bm_agreement_info修改失败");
                    }
                    return AjaxResult.success();
                }
            } else {
                //通过
                int count = sltAgreementInfoMapper.costExame(sltAgreementApply);
                if (count == GlobalConstants.INT_0) {
                    return AjaxResult.error("结算审批失败");
                } else {
                    return AjaxResult.success();
                }
            }
        } catch (Exception e) {
            return AjaxResult.error();
        }
    }

    @Override
    public SltInfoVo getSltRecordDetailsList(SltAgreementInfo info) {
        SltInfoVo sltInfoVo = new SltInfoVo();
        sltInfoVo.setUnitName(info.getUnitName());
        sltInfoVo.setProjectName(info.getProjectName());
        BigDecimal leaseCost = BigDecimal.ZERO;
        BigDecimal repairCost = BigDecimal.ZERO;
        BigDecimal scrapCost = BigDecimal.ZERO;
        BigDecimal loseCost = BigDecimal.ZERO;
        BigDecimal reductionCost = BigDecimal.ZERO;
        //租赁费用列表
        List<SltAgreementInfo> leaseList = getLeaseList(info);
        //维修费用列表
        List<SltAgreementInfo> repairList = getRepairList(info);
        //报废费用列表
        List<SltAgreementInfo> scrapList = getScrapList(info);
        //丢失费用列表
        List<SltAgreementInfo> loseList = getLoseList(info);
        //减免费用列表
        List<SltAgreementReduce> reductionList = getReductionList(info);
        sltInfoVo.setLeaseList(leaseList);
        sltInfoVo.setRepairList(repairList);
        sltInfoVo.setScrapList(scrapList);
        sltInfoVo.setLoseList(loseList);
        sltInfoVo.setReductionList(reductionList);

        for (SltAgreementInfo lease : leaseList) {
            if(lease.getCosts()!=null){
                leaseCost = leaseCost.add(lease.getCosts());
            }
        }
        for (SltAgreementInfo repair : repairList) {
            if(repair.getCosts()!=null && (repair.getPartType().equals("收费"))){
                repairCost = repairCost.add(repair.getCosts());
            }
        }
        for (SltAgreementInfo scrap : scrapList) {
            if(scrap.getCosts()!=null && (scrap.getPartType().equals("收费"))){
                scrapCost = scrapCost.add(scrap.getCosts());
            }
        }
        for (SltAgreementInfo lose : loseList) {
            if(lose.getCosts()!=null){
                loseCost = loseCost.add(lose.getCosts());
            }
        }

        for (SltAgreementReduce reduction : reductionList){
            if(reduction.getLeaseMoney()!=null){
                reductionCost = reductionCost.add(reduction.getLeaseMoney());
            }
        }
        sltInfoVo.setLeaseCost(leaseCost);
        sltInfoVo.setRepairCost(repairCost);
        sltInfoVo.setScrapCost(scrapCost);
        sltInfoVo.setLoseCost(loseCost);
        sltInfoVo.setReductionCost(reductionCost);
        List<SltAgreementRelation> relations = getRelations(leaseList, repairList, scrapList, loseList, info);
        //加上减免
        if (!relations.isEmpty()){
            relations.get(0).setSubCost(reductionCost);
        }
        sltInfoVo.setRelations(relations);
        return sltInfoVo;
    }

    /**
     * 工程类型树
     * @param bmProject
     * @return
     */
    @Override
    public AjaxResult getProjectListByUnitIds(BmProject bmProject) {
        List<ProjectTreeNode> groupList = new ArrayList<>();
        List<ProjectTreeNode> list = new ArrayList<>();
        try {
            if (bmProject.getUnitIds() != null) {
                Map<Integer,Integer> map = new HashMap<>();
                for (int i = 0; i < bmProject.getUnitIds().length; i++) {
                    int[] projectIds= sltAgreementInfoMapper.getProjectListByUnitIds(bmProject.getUnitIds()[i]);
                    Map<Integer,Integer> mapTemp = new HashMap<>();
                    for (int projectId : projectIds) {
                        mapTemp.put(projectId, 1);
                    }
                    mapTemp.forEach((k,v)-> {
                        if (map.containsKey(k)) {
                            map.put(k, map.get(k) + v);
                        } else {
                            map.put(k, v);
                        }
                    });
                }
                if(!map.isEmpty()){
                    List<Integer> keys = new ArrayList<>();
                    for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
                        if (entry.getValue() == bmProject.getUnitIds().length) {
                            keys.add(entry.getKey());
                        }
                    }
                    for (Integer key : keys) {
                        List<ProjectTreeNode> listTemp = sltAgreementInfoMapper.getProjectList(key);
                        for (ProjectTreeNode node : listTemp) {
                            if (!list.contains(node)) {
                                list.add(node);
                            }
                        }
                    }
                }
            }
            if (CollectionUtils.isNotEmpty(list)) {
                // 创建树形结构（数据集合作为参数）
                ProjectTreeBuild treeBuild = new ProjectTreeBuild(list);
                // 原查询结果转换树形结构
                groupList = treeBuild.buildTree();
            }
        } catch (Exception e) {
            return AjaxResult.error(e+"工程下拉树查询失败");
        }
        return AjaxResult.success(groupList);
    }


    @Override
    public AjaxResult getAgreementInfoById(SelectDto dto) {
       List<AgreementVo> vo;
        try {
            List<AgreementVo> list = sltAgreementInfoMapper.getAgreementInfoById(dto.getUnitIds(), Integer.parseInt(dto.getProjectId()));
            if (CollectionUtils.isNotEmpty(list)) {
                vo = list;
            } else {
                return AjaxResult.error("未找到匹配的协议信息");
            }
        } catch (Exception e) {
            return AjaxResult.error(e+"协议查询失败");
        }
        return AjaxResult.success(vo);
    }

    /**
     * 查询区间费用计算结果
     *
     * @param queryDto 查询条件
     * @return 区间费用计算结果列表
     */
    @Override
    public List<PeriodCostResultVo> selectPeriodCostList(@NotNull(message = "查询条件不能为空") PeriodCostQueryDto queryDto) {
        // 参数校验
        if (queryDto.getStartDate() == null || queryDto.getEndDate() == null) {
            throw new ServiceException("开始日期和结束日期不能为空");
        }

        if (queryDto.getStartDate().after(queryDto.getEndDate())) {
            throw new ServiceException("开始日期不能大于结束日期");
        }

        try {
            if (queryDto.getSltManageType() == null) {
                // 查询当前登陆用户的结算管理权限
                int settlementType = this.checkLoginUserHasSettlementPermission();
                queryDto.setSltManageType(settlementType);
            }

            List<PeriodCostResultVo> rawResults;
            if (StringUtils.isNotBlank(queryDto.getSettlementStatus()) && queryDto.getSettlementStatus().equals("2")) {
                // 从数据库slt_agreement_details表查询【已结算明细】
                rawResults = sltAgreementInfoMapper.selectLeasePeriodCostListBySltDetails(queryDto);
            } else {
                // 从数据库slt_agreement_info表查询【基础数据】
                rawResults = sltAgreementInfoMapper.selectLeasePeriodCostList(queryDto);
            }

            // 从数据库查询【冲账数据】,冲账查询的时候直接过滤了协议和限制具体日期，所以不需要再进行范围过滤
            List<PeriodCostResultVo> markResults = sltAgreementInfoMapper.selectPeriodCostListForCharge(queryDto);

            // 先过滤掉【基础数据】不在查询范围内的数据
            List<PeriodCostResultVo> filteredResults = filterDataInRange(rawResults, queryDto.getStartDate(), queryDto.getEndDate());

            // 进行时间及费用计算统计
            List<PeriodCostResultVo> periodCostResultVos = calculatePeriodCosts(filteredResults, queryDto.getStartDate(), queryDto.getEndDate());

            // 冲账数据不进行计算，直接计入结果
            periodCostResultVos.addAll(markResults);

            // 返回结果
            return periodCostResultVos;
        } catch (Exception e) {
            log.error("查询区间费用失败：", e);
            throw new ServiceException("查询区间费用失败：" + e.getMessage());
        }
    }

    /**
     * 过滤掉不在查询范围内的数据
     *
     * @param rawResults 原始查询结果
     * @param inputStartDate 前端输入的开始日期
     * @param inputEndDate 前端输入的结束日期
     * @return 过滤后的结果列表
     */
    private List<PeriodCostResultVo> filterDataInRange(List<PeriodCostResultVo> rawResults, Date inputStartDate, Date inputEndDate) {
        List<PeriodCostResultVo> filteredResults = new ArrayList<>();

        for (PeriodCostResultVo result : rawResults) {
            if (isDataInRange(result, inputStartDate, inputEndDate)) {
                filteredResults.add(result);
            }
        }

        return filteredResults;
    }

    /**
     * 判断数据是否在查询范围内（与SQL逻辑保持一致）
     *
     * @param result 数据记录
     * @param inputStartDate 前端输入的开始日期
     * @param inputEndDate 前端输入的结束日期
     * @return 是否在范围内
     */
    private boolean isDataInRange(PeriodCostResultVo result, Date inputStartDate, Date inputEndDate) {
        Date leaseStartTime = result.getStartTime();      // 领料时间
        Date returnTime = result.getEndTime();            // 退料时间
        Date settlementTime = result.getSettlementTime(); // 结算时间
        String status = result.getStatus();               // 设备状态

        // 如果是已结算协议
        if ("1".equals(result.getIsSettled())) {
            // 情况1：领料时间在查询区间内
            if (isDateInRange(leaseStartTime, inputStartDate, inputEndDate)) {
                return true;
            }

            // 情况2：退料时间在查询区间内
            if (isDateInRange(returnTime, inputStartDate, inputEndDate)) {
                return true;
            }

            // 情况3：结算时间在查询区间内
            if (isDateInRange(settlementTime, inputStartDate, inputEndDate)) {
                return true;
            }

            // 情况4：跨区间租赁（领料在区间前，退料/结算在区间后）
            if (leaseStartTime != null && leaseStartTime.before(inputStartDate)) {
                if ((returnTime != null && returnTime.after(inputEndDate)) ||
                    (settlementTime != null && settlementTime.after(inputEndDate))) {
                    return true;
                }
            }

            return false;
        } else {
            // 如果是未结算协议
            // 情况1：领料时间在查询区间内
            if (isDateInRange(leaseStartTime, inputStartDate, inputEndDate)) {
                return true;
            }

            // 情况2：退料时间在查询区间内
            if (isDateInRange(returnTime, inputStartDate, inputEndDate)) {
                return true;
            }

            // 情况3：跨区间租赁（领料在区间前，退料在区间后或未退料）
            if (leaseStartTime != null && leaseStartTime.before(inputStartDate)) {
                if (returnTime == null || returnTime.after(inputEndDate)) {
                    return true;
                }
            }

            // 情况4：设备状态为在用（未退料）且领料时间不晚于查询结束时间
            if ("0".equals(status) && leaseStartTime != null && !leaseStartTime.after(inputEndDate)) {
                return true;
            }

            return false;
        }
    }

    /**
     * 判断日期是否在指定范围内
     *
     * @param date 要检查的日期
     * @param startDate 范围开始日期
     * @param endDate 范围结束日期
     * @return 是否在范围内
     */
    private boolean isDateInRange(Date date, Date startDate, Date endDate) {
        if (date == null) {
            return false;
        }

        return !date.before(startDate) && !date.after(endDate);
    }

    /**
     * 计算区间费用
     *
     * @param filteredResults 过滤后的查询结果
     * @param inputStartDate 前端输入的开始日期
     * @param inputEndDate 前端输入的结束日期
     * @return 计算后的结果列表
     */
    private List<PeriodCostResultVo> calculatePeriodCosts(List<PeriodCostResultVo> filteredResults, Date inputStartDate, Date inputEndDate) {
        List<PeriodCostResultVo> calculatedResults = new ArrayList<>();

        for (PeriodCostResultVo result : filteredResults) {
            try {
                // 计算实际的开始和结束时间
                Date calcStartTime;
                Date calcEndTime;

                if ("1".equals(result.getIsSettled())) {
                    // 已结算协议计算规则
                    calcStartTime = calculateSettledStartTime(result, inputStartDate);
                    calcEndTime = calculateSettledEndTime(result, inputEndDate);
                } else {
                    // 未结算协议计算规则
                    calcStartTime = calculateUnsettledStartTime(result, inputStartDate);
                    calcEndTime = calculateUnsettledEndTime(result, inputEndDate);
                }

                // 确保计算时间在输入区间内且合理
                if (calcStartTime != null && calcEndTime != null && !calcStartTime.after(calcEndTime)) {
                    // 计算租赁天数
                    long leaseDays = calculateDaysBetween(calcStartTime, calcEndTime);

                    // 计算租赁费用
                    BigDecimal leaseCost = BigDecimal.ZERO;
                    if (result.getComsumable() != null && result.getComsumable() == 1) {
                        if (result.getNum() == null) {
                            result.setNum(BigDecimal.ZERO);
                        }
                        if (isDateInRange(result.getStartTime(), inputStartDate, inputEndDate)) {
                            // 消耗性物资直接计算购置价
                            result.setConsumeCost(result.getNum().multiply(Optional.ofNullable(result.getBuyPrice()).orElse(BigDecimal.ZERO)));
                        }
                    }
                    if (result.getNum() != null && result.getLeasePrice() != null && leaseDays >= 1) {
                        // 非消耗性物资按照租赁单价计算租赁费用
                        // 修复：改为 >= 1，确保当天领当天退（1天）也能正常计算费用
                        leaseCost = result.getNum().multiply(result.getLeasePrice()).multiply(new BigDecimal(leaseDays)).setScale(2, RoundingMode.HALF_UP);
                    }

                    // 设置计算结果
                    result.setCalcStartTime(calcStartTime);
                    result.setCalcEndTime(calcEndTime);
                    result.setLeaseDays(leaseDays);
                    result.setLeaseCost(Optional.of(leaseCost).orElse(BigDecimal.ZERO));
                    if (result.getConsumeCost() == null || result.getConsumeCost().compareTo(BigDecimal.ZERO) == 0) {
                        result.setConsumeCost(BigDecimal.ZERO);
                    }

                    calculatedResults.add(result);
                }

            } catch (Exception e) {
                // 记录错误但不中断处理
                System.err.println("计算协议 " + result.getAgreementCode() + " 费用时出错: " + e.getMessage());
            }
        }

        return calculatedResults;
    }

    /**
     * 计算已结算协议的开始时间
     */
    private Date calculateSettledStartTime(PeriodCostResultVo result, Date inputStartDate) {
        Date leaseStartTime = result.getStartTime(); // 领料时间

        if (leaseStartTime == null) {
            return inputStartDate;
        }

        // 如果领料时间在输入区间开始之前，使用输入开始时间
        if (leaseStartTime.before(inputStartDate)) {
            return inputStartDate;
        }

        // 否则使用领料时间
        return leaseStartTime;
    }

    /**
     * 计算已结算协议的结束时间
     */
    private Date calculateSettledEndTime(PeriodCostResultVo result, Date inputEndDate) {
        Date settlementTime = result.getSettlementTime(); // 结算时间
        Date returnTime = result.getEndTime(); // 退料时间

        // 如果有退料时间，优先使用退料时间
        if (returnTime != null) {
            // 如果退料时间在输入区间结束之后，使用输入结束时间
            if (returnTime.after(inputEndDate)) {
                return inputEndDate;
            }
            return returnTime;
        }

        // 如果没有退料时间但有结算时间，使用结算时间作为退料时间
        if (settlementTime != null) {
            // 如果结算时间在输入区间结束之前，使用结算时间
            if (settlementTime.before(inputEndDate) || settlementTime.equals(inputEndDate)) {
                return settlementTime;
            }
            // 如果结算时间在输入区间结束之后，使用输入结束时间
            return inputEndDate;
        }

        // 如果既没有退料时间也没有结算时间，使用输入结束时间
        return inputEndDate;
    }

    /**
     * 计算未结算协议的开始时间
     */
    private Date calculateUnsettledStartTime(PeriodCostResultVo result, Date inputStartDate) {
        Date leaseStartTime = result.getStartTime(); // 领料时间

        if (leaseStartTime == null) {
            return inputStartDate;
        }

        // 如果领料时间在输入区间开始之前，使用输入开始时间
        if (leaseStartTime.before(inputStartDate)) {
            return inputStartDate;
        }

        // 否则使用领料时间
        return leaseStartTime;
    }

    /**
     * 计算未结算协议的结束时间
     */
    private Date calculateUnsettledEndTime(PeriodCostResultVo result, Date inputEndDate) {
        Date returnTime = result.getEndTime(); // 退料时间

        // 如果有退料时间
        if (returnTime != null) {
            // 如果退料时间在输入区间结束之后，使用输入结束时间
            if (returnTime.after(inputEndDate)) {
                return inputEndDate;
            }
            return returnTime;
        }

        // 如果没有退料时间，使用输入结束时间
        return inputEndDate;
    }

    /**
     * 计算两个日期之间的天数（包含起始和结束日期）
     * 只计算日期，不考虑具体时间
     * 例如：8月15日～8月16日 = 2天，当天领当天退 = 1天
     * 修复说明：确保当领料日期和退料日期相同时，无论时间如何，都算作1天
     *
     * @param startDate 开始日期
     * @param endDate 结束日期
     * @return 天数
     */
    private long calculateDaysBetween(Date startDate, Date endDate) {
        if (startDate == null || endDate == null) {
            return 0;
        }

        if (startDate.after(endDate)) {
            return 0;
        }

        // 使用Calendar来确保只计算日期部分，完全忽略时间
        Calendar startCal = Calendar.getInstance();
        startCal.setTime(startDate);
        // 将开始时间设为当天00:00:00.000
        startCal.set(Calendar.HOUR_OF_DAY, 0);
        startCal.set(Calendar.MINUTE, 0);
        startCal.set(Calendar.SECOND, 0);
        startCal.set(Calendar.MILLISECOND, 0);

        Calendar endCal = Calendar.getInstance();
        endCal.setTime(endDate);
        // 将结束时间设为当天00:00:00.000
        endCal.set(Calendar.HOUR_OF_DAY, 0);
        endCal.set(Calendar.MINUTE, 0);
        endCal.set(Calendar.SECOND, 0);
        endCal.set(Calendar.MILLISECOND, 0);

        // 计算天数差异（现在比较的是日期，不包含时间）
        long diffInMillies = endCal.getTimeInMillis() - startCal.getTimeInMillis();
        long diffInDays = diffInMillies / (24 * 60 * 60 * 1000);

        // 包含起始和结束日期，所以要加1
        // 即使是同一天（diffInDays=0），加1后也是1天，符合需求
        return diffInDays + 1;
    }

    /**
     * 查询区间费用汇总结果（按协议汇总）
     *
     * @param queryDto 查询条件
     * @return 区间费用汇总结果列表
     */
    @Override
    public List<PeriodCostSummaryVo> selectPeriodCostSummary(PeriodCostQueryDto queryDto) {
        // 参数校验
        if (queryDto.getStartDate() == null || queryDto.getEndDate() == null) {
            throw new ServiceException("开始日期和结束日期不能为空");
        }

        if (queryDto.getStartDate().after(queryDto.getEndDate())) {
            throw new ServiceException("开始日期不能大于结束日期");
        }

        try {
            // 先获取明细数据
            List<PeriodCostResultVo> detailList = this.selectPeriodCostList(queryDto);

            // 按协议ID分组汇总
            return aggregateByAgreement(detailList, queryDto.getStartDate(), queryDto.getEndDate());
        } catch (Exception e) {
            System.err.println(e);
            throw new ServiceException("查询区间费用汇总失败：" + e.getMessage());
        }
    }

    /**
     * 按协议汇总费用数据
     *
     * @param detailList 明细数据列表
     * @param queryStartDate 查询开始日期
     * @param queryEndDate 查询结束日期
     * @return 汇总结果列表
     */
    private List<PeriodCostSummaryVo> aggregateByAgreement(List<PeriodCostResultVo> detailList,
                                                          Date queryStartDate, Date queryEndDate) {
        // 按协议ID分组
        Map<Long, List<PeriodCostResultVo>> groupedByAgreement = detailList.stream()
            .collect(Collectors.groupingBy(PeriodCostResultVo::getAgreementId));

        List<PeriodCostSummaryVo> summaryList = new ArrayList<>();

        for (Map.Entry<Long, List<PeriodCostResultVo>> entry : groupedByAgreement.entrySet()) {
            Long agreementId = entry.getKey();
            List<PeriodCostResultVo> agreementDetails = entry.getValue();

            if (agreementDetails.isEmpty()) {
                continue;
            }

            // 取第一条记录的基本信息
            PeriodCostResultVo firstDetail = agreementDetails.get(0);

            PeriodCostSummaryVo summary = new PeriodCostSummaryVo();
            summary.setAgreementId(agreementId);
            summary.setAgreementCode(firstDetail.getAgreementCode());
            summary.setUnitId(firstDetail.getUnitId());
            summary.setUnitName(firstDetail.getUnitName());
            summary.setProjectId(firstDetail.getProjectId());
            summary.setProjectName(firstDetail.getProjectName());
            summary.setIsSettled(firstDetail.getIsSettled());
            summary.setQueryStartDate(queryStartDate);
            summary.setQueryEndDate(queryEndDate);
            summary.setRemark(firstDetail.getRemark());

            // 计算汇总数据
            int equipmentTypeCount = agreementDetails.size(); // 设备种类数量
            BigDecimal totalEquipmentCount = agreementDetails.stream()
                .map(detail -> detail.getNum() != null ? detail.getNum() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add); // 设备总数量

            long totalLeaseDays = agreementDetails.stream()
                .mapToLong(detail -> detail.getLeaseDays() != null ? detail.getLeaseDays() : 0L)
                .sum(); // 总租赁天数

            BigDecimal totalLeaseCost = agreementDetails.stream()
                .map(detail -> detail.getLeaseCost() != null ? detail.getLeaseCost() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add); // 总租赁费用

            BigDecimal totalConsumeCost = agreementDetails.stream()
                    .map(detail -> detail.getConsumeCost() != null ? detail.getConsumeCost() : BigDecimal.ZERO)
                    .reduce(BigDecimal.ZERO, BigDecimal::add); // 统计总消耗费用

            // 计算平均日租金
            BigDecimal avgDailyRent = BigDecimal.ZERO;
            if (totalLeaseDays > 0) {
                avgDailyRent = totalLeaseCost.divide(new BigDecimal(totalLeaseDays), 2, RoundingMode.HALF_UP);
            }

            // 找出最早领料时间和最晚退料时间
            Date earliestLeaseTime = agreementDetails.stream()
                .map(PeriodCostResultVo::getStartTime)
                .filter(Objects::nonNull)
                .min(Date::compareTo)
                .orElse(null);

            Date latestReturnTime = agreementDetails.stream()
                .map(PeriodCostResultVo::getEndTime)
                .filter(Objects::nonNull)
                .max(Date::compareTo)
                .orElse(null);

            Date settlementTime = agreementDetails.stream()
                .map(PeriodCostResultVo::getSettlementTime)
                .filter(Objects::nonNull)
                .findFirst()
                .orElse(null);

            summary.setEquipmentTypeCount(equipmentTypeCount);
            summary.setTotalEquipmentCount(totalEquipmentCount);
            summary.setTotalLeaseDays(totalLeaseDays);
            summary.setTotalLeaseCost(totalLeaseCost);
            summary.setTotalConsumeCost(totalConsumeCost);
            summary.setAvgDailyRent(avgDailyRent);
            summary.setEarliestLeaseTime(earliestLeaseTime);
            summary.setLatestReturnTime(latestReturnTime);
            summary.setSettlementTime(settlementTime);

            summaryList.add(summary);
        }

        if (!summaryList.isEmpty()) {
            List<PeriodCostSummaryVo> collect = summaryList.stream().filter(item -> Objects.isNull(item.getAgreementCode())).collect(Collectors.toList());
            if (!collect.isEmpty()) {
                throw new ServiceException("存在未填写协议编号的协议");
            }
        }

        // 按协议编号排序
        summaryList.sort(Comparator.comparing(PeriodCostSummaryVo::getAgreementCode));

        return summaryList;
    }
}
