package com.engine.salary.service.impl;

import com.engine.common.util.ServiceUtil;
import com.engine.core.impl.Service;
import com.engine.hrmelog.entity.dto.LoggerContext;
import com.engine.salary.cache.SalaryCacheKey;
import com.engine.salary.common.LocalDateRange;
import com.engine.salary.config.SalaryElogConfig;
import com.engine.salary.constant.SalaryDefaultTenantConstant;
import com.engine.salary.encrypt.EncryptUtil;
import com.engine.salary.entity.datacollection.DataCollectionEmployee;
import com.engine.salary.entity.datacollection.dto.AttendQuoteFieldListDTO;
import com.engine.salary.entity.progress.ProgressDTO;
import com.engine.salary.entity.report.bo.SalaryAcctResultReportBO;
import com.engine.salary.entity.report.po.SalaryAcctResultReportPO;
import com.engine.salary.entity.salaryacct.bo.*;
import com.engine.salary.entity.salaryacct.dto.ConsolidatedTaxDetailDTO;
import com.engine.salary.entity.salaryacct.dto.SalaryAcctResultDetailDTO;
import com.engine.salary.entity.salaryacct.dto.SalaryAcctResultListColumnDTO;
import com.engine.salary.entity.salaryacct.param.*;
import com.engine.salary.entity.salaryacct.po.*;
import com.engine.salary.entity.salaryformula.ExpressFormula;
import com.engine.salary.entity.salaryitem.po.SalaryItemPO;
import com.engine.salary.entity.salarysob.dto.*;
import com.engine.salary.entity.salarysob.po.*;
import com.engine.salary.entity.taxagent.po.TaxAgentAdminPO;
import com.engine.salary.entity.taxagent.po.TaxAgentPO;
import com.engine.salary.enums.OperateTypeEnum;
import com.engine.salary.enums.SalaryValueTypeEnum;
import com.engine.salary.enums.UserStatusEnum;
import com.engine.salary.enums.common.FilterEnum;
import com.engine.salary.enums.salaryaccounting.LockStatusEnum;
import com.engine.salary.enums.salaryaccounting.SalaryAcctRecordStatusEnum;
import com.engine.salary.enums.salaryaccounting.SalaryAcctResultDataSourceEnum;
import com.engine.salary.enums.salaryitem.SalaryDataTypeEnum;
import com.engine.salary.enums.salarysob.IncomeCategoryEnum;
import com.engine.salary.exception.SalaryRunTimeException;
import com.engine.salary.mapper.salaryacct.SalaryAcctResultMapper;
import com.engine.salary.report.service.SalaryStatisticsReportService;
import com.engine.salary.report.service.impl.SalaryStatisticsReportServiceImpl;
import com.engine.salary.service.*;
import com.engine.salary.sys.constant.SalarySysConstant;
import com.engine.salary.sys.entity.po.SalarySysConfPO;
import com.engine.salary.sys.enums.TaxDeclarationFunctionEnum;
import com.engine.salary.sys.service.SalarySysConfService;
import com.engine.salary.sys.service.impl.SalarySysConfServiceImpl;
import com.engine.salary.util.SalaryDateUtil;
import com.engine.salary.util.SalaryEntityUtil;
import com.engine.salary.util.SalaryI18nUtil;
import com.engine.salary.util.db.IdGenerator;
import com.engine.salary.util.db.MapperProxyFactory;
import com.engine.salary.util.page.PageInfo;
import com.engine.salary.util.page.SalaryPageUtil;
import com.engine.salary.util.valid.ValidUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.weaver.util.threadPool.ThreadPoolUtil;
import com.weaver.util.threadPool.constant.ModulePoolEnum;
import com.weaver.util.threadPool.entity.LocalRunnable;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.util.StopWatch;
import weaver.hrm.User;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.stream.Collectors;

/**
 * 薪资核算结果
 * <p>Copyright: Copyright (c) 2022</p>
 * <p>Company: 泛微软件</p>
 *
 * @author qiantao
 * @version 1.0
 **/
@Slf4j
public class SalaryAcctResultServiceImpl extends Service implements SalaryAcctResultService {
    private EncryptUtil encryptUtil = new EncryptUtil();

    private SalaryAcctResultMapper getSalaryAcctResultMapper() {
        return MapperProxyFactory.getProxy(SalaryAcctResultMapper.class);
    }

    private SalaryAcctEmployeeService getSalaryAcctEmployeeService(User user) {
        return ServiceUtil.getService(SalaryAcctEmployeeServiceImpl.class, user);
    }

    private SalarySobItemService getSalarySobItemService(User user) {
        return ServiceUtil.getService(SalarySobItemServiceImpl.class, user);
    }

    private SalaryItemService getSalaryItemService(User user) {
        return ServiceUtil.getService(SalaryItemServiceImpl.class, user);
    }

    private SalarySobEmpFieldService getSalarySobEmpFieldService(User user) {
        return ServiceUtil.getService(SalarySobEmpFieldServiceImpl.class, user);
    }

    private SalarySobService getSalarySobService(User user) {
        return ServiceUtil.getService(SalarySobServiceImpl.class, user);
    }

    private SalaryAcctRecordService getSalaryAcctRecordService(User user) {
        return ServiceUtil.getService(SalaryAcctRecordServiceImpl.class, user);
    }

    private TaxAgentService getTaxAgentService(User user) {
        return ServiceUtil.getService(TaxAgentServiceImpl.class, user);
    }

    private SalaryEmployeeService getSalaryEmployeeService(User user) {
        return ServiceUtil.getService(SalaryEmployeeServiceImpl.class, user);
    }

    private SalaryFormulaService getSalaryFormulaService(User user) {
        return ServiceUtil.getService(SalaryFormulaServiceImpl.class, user);
    }

    private SalarySobAdjustRuleService getSalarySobAdjustRuleService(User user) {
        return ServiceUtil.getService(SalarySobAdjustRuleServiceImpl.class, user);
    }

    private SalarySobItemHideService getSalarySobItemHideService(User user) {
        return (SalarySobItemHideService) ServiceUtil.getService(SalarySobItemHideServiceImpl.class, user);
    }

    private SalaryAcctCalculateService getSalaryAcctCalculateService(User user) {
        return ServiceUtil.getService(SalaryAcctCalculateServiceImpl.class, user);
    }

    private ProgressService getSalaryAcctProgressService(User user) {
        return ServiceUtil.getService(ProgressServiceImpl.class, user);
    }

    private DataSourceTransactionManager dataSourceTransactionManager;

    private SalaryAcctResultTempService getSalaryAcctResultTempService(User user) {
        return ServiceUtil.getService(SalaryAcctResultTempServiceImpl.class, user);
    }

    private SIAccountService getSIAccountService(User user) {
        return ServiceUtil.getService(SIAccountServiceImpl.class, user);
    }

    private AttendQuoteFieldService getAttendQuoteFieldService(User user) {
        return ServiceUtil.getService(AttendQuoteFieldServiceImpl.class, user);
    }

    private SalaryAcctReportService getSalaryAcctReportService(User user) {
        return ServiceUtil.getService(SalaryAcctReportServiceImpl.class, user);
    }

    private SalarySobItemGroupService getSalarySobItemGroupService(User user) {
        return (SalarySobItemGroupService) ServiceUtil.getService(SalarySobItemGroupServiceImpl.class, user);
    }

    private SalarySobBackItemService getSalarySobBackItemService(User user) {
        return ServiceUtil.getService(SalarySobBackItemServiceImpl.class, user);
    }

    private SalaryAcctResultService getSalaryAcctResultService(User user) {
        return (SalaryAcctResultService) ServiceUtil.getService(SalaryAcctResultServiceImpl.class, user);
    }

    private SalaryAcctResultLogService getSalaryAcctResultLogService(User user) {
        return ServiceUtil.getService(SalaryAcctResultLogServiceImpl.class, user);
    }

    private SalarySysConfService getSalarySysConfService(User user) {
        return ServiceUtil.getService(SalarySysConfServiceImpl.class, user);
    }

    private SalaryStatisticsReportService getSalaryStatisticsReportService(User user) {
        return ServiceUtil.getService(SalaryStatisticsReportServiceImpl.class, user);
    }

    private TaxAgentAdminService getTaxAgentAdminService(User user) {
        return ServiceUtil.getService(TaxAgentAdminServiceImpl.class, user);
    }


    private SalaryCheckResultService salaryCheckResultService;

    @Override
    public List<SalaryAcctResultPO> listBySalaryAcctRecordIds(Collection<Long> salaryAcctRecordIds) {
        if (CollectionUtils.isEmpty(salaryAcctRecordIds)) {
            return Collections.emptyList();
        }
        List<SalaryAcctResultPO> salaryAcctResultPOS = getSalaryAcctResultMapper().listSome(SalaryAcctResultPO.builder().salaryAcctRecordIds(salaryAcctRecordIds).build());
        encryptUtil.decryptList(salaryAcctResultPOS, SalaryAcctResultPO.class);
        return salaryAcctResultPOS;
    }

    @Override
    public List<SalaryAcctResultPO> listBySalaryAcctEmployeeId(Long salaryAcctEmployeeId) {
        List<SalaryAcctResultPO> salaryAcctResultPOS = getSalaryAcctResultMapper().listSome(SalaryAcctResultPO.builder().salaryAcctEmpId(salaryAcctEmployeeId).build());
        encryptUtil.decryptList(salaryAcctResultPOS, SalaryAcctResultPO.class);
        // 解密
        return salaryAcctResultPOS;
    }

    @Override
    public List<SalaryAcctResultPO> listBySalaryAcctEmployeeIds(Collection<Long> salaryAcctEmployeeIds) {
        if (CollectionUtils.isEmpty(salaryAcctEmployeeIds)) {
            return Collections.emptyList();
        }
        List<List<Long>> partition = Lists.partition((List<Long>) salaryAcctEmployeeIds, 500);
        List<SalaryAcctResultPO> salaryAcctResultPOS = new ArrayList<>();
        partition.forEach(empIds -> {
            salaryAcctResultPOS.addAll(getSalaryAcctResultMapper().listSome(SalaryAcctResultPO.builder().salaryAcctEmpIds(empIds).build()));
        });
        // 解密
        encryptUtil.decryptList(salaryAcctResultPOS, SalaryAcctResultPO.class);
        return salaryAcctResultPOS;
    }

    @Override
    public List<SalaryAcctResultPO> listBySalaryAcctRecordIdsAndEmployeeIds(Collection<Long> salaryAcctRecordIds, Collection<Long> employeeIds) {
        if (CollectionUtils.isEmpty(salaryAcctRecordIds) || CollectionUtils.isEmpty(employeeIds)) {
            return Collections.emptyList();
        }
        List<List<Long>> partition = Lists.partition((List<Long>) employeeIds, 500);
        List<SalaryAcctResultPO> salaryAcctResultPOS = new ArrayList<>();
        partition.forEach(empIds -> {
            salaryAcctResultPOS.addAll(getSalaryAcctResultMapper().listSome(SalaryAcctResultPO.builder().salaryAcctRecordIds(salaryAcctRecordIds).employeeIds(empIds).build()));
        });
        encryptUtil.decryptList(salaryAcctResultPOS, SalaryAcctResultPO.class);
        return salaryAcctResultPOS;
    }

    @Override
    public SalaryAcctResultDetailDTO getBySalaryAcctEmployeeId(Long salaryAcctEmployeeId) {
        // 查询薪资核算人员
        SalaryAcctEmployeePO salaryAcctEmployeePO = getSalaryAcctEmployeeService(user).getById(salaryAcctEmployeeId);
        if (Objects.isNull(salaryAcctEmployeePO)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98831, "薪资核算人员不存在或已被删除"));
        }
        // 查询是否是回算
        SalaryAcctRecordPO byId = getSalaryAcctRecordService(user).getById(salaryAcctEmployeePO.getSalaryAcctRecordId());
        boolean isBackCal = Objects.equals(byId.getBackCalcStatus(), 1);
        //----------------------------------------------

        // 查询薪资账套的薪资项目分类
        List<SalarySobItemGroupPO> salarySobItemGroupPOS = getSalarySobItemGroupService(user).listBySalarySobIdWithItemHide(salaryAcctEmployeePO.getSalarySobId());
        // 获取关闭显示的分类
        List<Long> hideGroupIDs = getSalarySobItemHideService(user).listHideGroupBysalarySobId(SalarySobItemHidePO.builder().salarySobId(salaryAcctEmployeePO.getSalarySobId()).isGroup(1).build());
        // 过滤关闭显示的薪资项目分类
        salarySobItemGroupPOS = salarySobItemGroupPOS.stream().filter(group -> !(hideGroupIDs.contains(group.getId()))).collect(Collectors.toList());
        // 获取薪资项目分类ID
        List<Long> salarySobItemGroupIds = salarySobItemGroupPOS.stream().map(SalarySobItemGroupPO::getId).collect(Collectors.toList());
        //加上未分类
        salarySobItemGroupIds.add(0L);

        // 查询薪资账套的薪资项目副本(已经过滤关闭分类显示按钮的薪资项目)
        List<SalarySobItemPO> salarySobItemPOS = getSalarySobItemService(user).listBySalarySobIdAndGroupId(salaryAcctEmployeePO.getSalarySobId(), salarySobItemGroupIds);
        //----------------------------------------------

        // 查询薪资核算所用薪资账套的薪资项目副本
//        List<SalarySobItemPO> salarySobItemPOS = getSalarySobItemService(user).listBySalarySobId(salaryAcctEmployeePO.getSalarySobId());
        // 获取关闭显示的薪资项目
        List<Long> hideItemIDs = getSalarySobItemHideService(user).listHideGroupBysalarySobId(SalarySobItemHidePO.builder().salarySobId(salaryAcctEmployeePO.getSalarySobId()).isGroup(0).build());
        // 过滤薪资项目
        salarySobItemPOS = salarySobItemPOS.stream().filter(item -> !(hideItemIDs.contains(item.getSalaryItemId()))).collect(Collectors.toList());

        // 查询已发补发薪资项目
        List<SalarySobBackItemPO> salarySobBackItemPOList = Collections.emptyList();
        List<SalaryItemPO> salaryBackItemPOS = Collections.emptyList();
        // key:账套中回算薪资项目的薪资项目id  value:账套中回算薪资项目所对应的公式内容
        Map<Long, String> salaryBackItemFormula = new HashMap<>();
        if (isBackCal) {
            salarySobBackItemPOList = getSalarySobBackItemService(user).listBySalarySobId(byId.getSalarySobId());
            Set<Long> salarySobBackItemIds = SalaryEntityUtil.properties(salarySobBackItemPOList, SalarySobBackItemPO::getSalaryItemId);
            salaryBackItemPOS = getSalaryItemService(user).listByIds(salarySobBackItemIds);
            // 查询薪资账套中已发补发的公式内容
            List<ExpressFormula> expressFormulas = getSalaryFormulaService(user).listExpressFormula(SalaryEntityUtil.properties(salarySobBackItemPOList, SalarySobBackItemPO::getFormulaId));
            Map<Long, String> expressFormulaMap = SalaryEntityUtil.convert2Map(expressFormulas, ExpressFormula::getId, ExpressFormula::getFormula);
            for (SalarySobBackItemPO salarySobBackItemPO : salarySobBackItemPOList) {
                String exp = Objects.equals(expressFormulaMap.get(salarySobBackItemPO.getFormulaId()), 0) ? "输入" : expressFormulaMap.get(salarySobBackItemPO.getFormulaId());
                salaryBackItemFormula.put(salarySobBackItemPO.getSalaryItemId(), exp);
            }
        }

        // 查询薪资项目
        Set<Long> salaryItemIds = SalaryEntityUtil.properties(salarySobItemPOS, SalarySobItemPO::getSalaryItemId);
        List<SalaryItemPO> salaryItemPOS = getSalaryItemService(user).listByIds(salaryItemIds);
        // 查询薪资核算所用薪资账套的人员信息字段
        List<SalarySobEmpFieldPO> salarySobEmpFieldPOS = getSalarySobEmpFieldService(user).listBySalarySobId(salaryAcctEmployeePO.getSalarySobId());
        // 查询人员信息
        DataCollectionEmployee simpleEmployee = getSalaryEmployeeService(user).getEmployeeById(salaryAcctEmployeePO.getEmployeeId());
        // 查询薪资核算结果
        List<SalaryAcctResultPO> salaryAcctResultPOS = listBySalaryAcctEmployeeId(salaryAcctEmployeeId);
        // 查询个税扣缴义务人
        TaxAgentPO taxAgent = getTaxAgentService(user).getById(salaryAcctEmployeePO.getTaxAgentId());
        // 查询公式
        Map<String, SalaryAcctResultListColumnDTO> formulaContentMap = getSalaryAcctResultService(user).getColumnBySalaryAcctRecordId(salaryAcctEmployeePO.getSalaryAcctRecordId());
        List<Long> lockItems = byId.getLockSalaryItemIds() == null ? Collections.emptyList() : byId.getLockSalaryItemIds();

        // 转换成薪资核算结果详情dto
        return SalaryAcctResultBO.convert2DetailDTO(simpleEmployee, taxAgent, salaryAcctEmployeePO, salarySobEmpFieldPOS, salarySobItemGroupPOS, salarySobItemPOS, salaryItemPOS, salaryAcctResultPOS, salarySobBackItemPOList, salaryBackItemPOS, salaryBackItemFormula, formulaContentMap, lockItems);
    }

    @Override
    public PageInfo<Map<String, Object>> listPageByParam(SalaryAcctResultQueryParam queryParam) {
        PageInfo<SalaryAcctEmployeePO> page = null;
        //其他条件
        List<SalaryAcctResultQueryParam.OtherCondition> otherConditions = queryParam.getOtherConditions();
        if (CollectionUtils.isNotEmpty(otherConditions)) {
            List<Long> items = SalaryEntityUtil.properties(otherConditions, SalaryAcctResultQueryParam.OtherCondition::getItemId, Collectors.toList());
            List<SalaryAcctResultPO> list = listBySalaryAcctRecordIdsAndSalaryItemIds(Collections.singletonList(queryParam.getSalaryAcctRecordId()), items);
            for (int i = 0; i < otherConditions.size(); i++) {
                SalaryAcctResultQueryParam.OtherCondition otherCondition = otherConditions.get(i);
                Long itemId = otherCondition.getItemId();
                FilterEnum filter = otherCondition.getFilter();
                List<String> params = otherCondition.getParams();
                list = list.stream().filter(a -> Objects.equals(a.getSalaryItemId(), itemId)).filter(a -> filter.filter(params).test(a.getResultValue())).collect(Collectors.toList());
            }
            List<Long> salaryAcctEmpId = SalaryEntityUtil.properties(list, SalaryAcctResultPO::getSalaryAcctEmpId, Collectors.toList());

            if (CollectionUtils.isEmpty(salaryAcctEmpId)) {
                //条件不满足直接返回空列表
                page = SalaryPageUtil.buildPage(queryParam.getCurrent(), queryParam.getPageSize(), new ArrayList<>(), SalaryAcctEmployeePO.class);
            } else {
                queryParam.setIds(salaryAcctEmpId);
                // 查询薪资核算人员（分页）
                page = getSalaryAcctEmployeeService(user).listPageByResultQueryParam(queryParam);
            }
        } else {
            // 查询薪资核算人员（分页）
            page = getSalaryAcctEmployeeService(user).listPageByResultQueryParam(queryParam);
        }


        // 查询薪资核算结果
        List<Map<String, Object>> data = listBySalaryAcctEmployees(page.getList(), queryParam);
        // 薪资核算结果的分页结果
        PageInfo<Map<String, Object>> resultPage = new PageInfo<>();
        resultPage.setList(data);
        resultPage.setTotal(page.getTotal());
        resultPage.setPageNum(page.getPageNum());
        resultPage.setPageSize(page.getPageSize());
        return resultPage;
    }

    @Override
    public Map<String, Object> sumRow(SalaryAcctResultQueryParam queryParam) {

        //其他条件
        List<SalaryAcctResultQueryParam.OtherCondition> otherConditions = queryParam.getOtherConditions();
        if (CollectionUtils.isNotEmpty(otherConditions)) {
            List<Long> items = SalaryEntityUtil.properties(otherConditions, SalaryAcctResultQueryParam.OtherCondition::getItemId, Collectors.toList());
            List<SalaryAcctResultPO> list = listBySalaryAcctRecordIdsAndSalaryItemIds(Collections.singletonList(queryParam.getSalaryAcctRecordId()), items);
            for (int i = 0; i < otherConditions.size(); i++) {
                SalaryAcctResultQueryParam.OtherCondition otherCondition = otherConditions.get(i);
                Long itemId = otherCondition.getItemId();
                FilterEnum filter = otherCondition.getFilter();
                List<String> params = otherCondition.getParams();
                list = list.stream().filter(a -> Objects.equals(a.getSalaryItemId(), itemId)).filter(a -> filter.filter(params).test(a.getResultValue())).collect(Collectors.toList());
            }
            List<Long> salaryAcctEmpId = SalaryEntityUtil.properties(list, SalaryAcctResultPO::getSalaryAcctEmpId, Collectors.toList());
            if (CollectionUtils.isEmpty(salaryAcctEmpId)) {
                //条件不满足直接返回空列表
                return new HashMap<>();
            } else {
                queryParam.setIds(salaryAcctEmpId);
            }
        }


        // 查询薪资核算人员
        List<SalaryAcctEmployeePO> salaryAcctEmployeePOS = getSalaryAcctEmployeeService(user).listByResultQueryParam(queryParam);

        if (CollectionUtils.isEmpty(salaryAcctEmployeePOS)) {
            return null;
        }
        // 查询薪资核算记录
        SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(queryParam.getSalaryAcctRecordId());
        if (Objects.isNull(salaryAcctRecordPO)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98747, "薪资核算记录不存在或已被删除"));
        }
        // 查询薪资核算所用薪资账套的薪资项目
        List<SalarySobItemPO> salarySobItemPOS = getSalarySobItemService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId());
        Map<Long, SalarySobItemPO> salaryItemIdKeySalarySobItemPOMap = SalaryEntityUtil.convert2Map(salarySobItemPOS, SalarySobItemPO::getSalaryItemId);
        Set<Long> salaryItemIds = SalaryEntityUtil.properties(salarySobItemPOS, SalarySobItemPO::getSalaryItemId);
        if (Objects.equals(salaryAcctRecordPO.getBackCalcStatus(), NumberUtils.INTEGER_ONE)) {
            // 是回算，获取回算项
            List<SalarySobBackItemPO> salarySobBackItemPOS = getSalarySobBackItemService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId());
            salaryItemIds.addAll(salarySobBackItemPOS.stream().map(SalarySobBackItemPO::getSalaryItemId).collect(Collectors.toList()));
        }
        List<SalaryItemPO> salaryItemPOS = getSalaryItemService(user).listByIds(salaryItemIds);

        // 查询薪资核算结果
        List<Long> salaryAcctEmployeeIds = SalaryEntityUtil.properties(salaryAcctEmployeePOS, SalaryAcctEmployeePO::getId, Collectors.toList());
        List<SalaryAcctResultPO> salaryAcctResultPOS = listBySalaryAcctEmployeeIds(salaryAcctEmployeeIds);

        Map<String, Object> map = new HashMap<>();
        Map<Long, List<SalaryAcctResultPO>> acctResultMap = SalaryEntityUtil.group2Map(salaryAcctResultPOS, SalaryAcctResultPO::getSalaryItemId);
        salaryItemPOS.stream().filter(item -> SalaryDataTypeEnum.NUMBER.getValue().equals(item.getDataType())).forEach(item -> {
            BigDecimal sum = Optional.ofNullable(acctResultMap.get(item.getId())).orElse(new ArrayList<>()).stream().map(SalaryAcctResultPO::getResultValue).filter(NumberUtils::isCreatable).map(BigDecimal::new).reduce(BigDecimal.ZERO, BigDecimal::add);
            map.put(item.getId().toString(), SalaryAcctFormulaBO.roundResultValue(sum.toString(), item, Collections.emptyList(), Collections.emptyMap(), salaryItemIdKeySalarySobItemPOMap));
        });
        return map;
    }

    @Override
    public List<Map<String, Object>> listByParam(SalaryAcctResultQueryParam queryParam) {
        // 查询薪资核算人员
        List<SalaryAcctEmployeePO> salaryAcctEmployeePOS = getSalaryAcctEmployeeService(user).listByResultQueryParam(queryParam);
        // 查询薪资核算结果
        return listBySalaryAcctEmployees(salaryAcctEmployeePOS, queryParam);
    }

    /**
     * 根据薪资核算人员查询薪资核算结果
     *
     * @param salaryAcctEmployeePOS 薪资核算人员
     * @param queryParam            列表查询条件
     * @return
     */
    private List<Map<String, Object>> listBySalaryAcctEmployees(List<SalaryAcctEmployeePO> salaryAcctEmployeePOS, SalaryAcctResultQueryParam queryParam) {
        if (CollectionUtils.isEmpty(salaryAcctEmployeePOS)) {
            return Collections.emptyList();
        }
        // 查询薪资核算记录
        SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(queryParam.getSalaryAcctRecordId());
        if (Objects.isNull(salaryAcctRecordPO)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98747, "薪资核算记录不存在或已被删除"));
        }
        boolean isBackCalc = Objects.equals(salaryAcctRecordPO.getBackCalcStatus(), 1);
        // 查询薪资核算所用的薪资账套的员工信息字段
        List<SalarySobEmpFieldPO> salarySobEmpFieldPOS = getSalarySobEmpFieldService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId());
        // 查询薪资核算所用薪资账套的薪资项目
        List<SalarySobItemPO> salarySobItemPOS = getSalarySobItemService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId());
        Set<Long> salaryItemIds = SalaryEntityUtil.properties(salarySobItemPOS, SalarySobItemPO::getSalaryItemId);
        // 是否是回算
        List<SalarySobBackItemPO> salarySobBackItemPOList = Collections.emptyList();
        if (isBackCalc) {
            // 根据薪资账套获取回算信息项
            salarySobBackItemPOList = getSalarySobBackItemService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId());
            salaryItemIds.addAll(salarySobBackItemPOList.stream().map(SalarySobBackItemPO::getSalaryItemId).collect(Collectors.toList()));
        }
        List<SalaryItemPO> salaryItemPOS = getSalaryItemService(user).listByIds(salaryItemIds);
        // 查询薪资核算结果
        List<Long> salaryAcctEmployeeIds = SalaryEntityUtil.properties(salaryAcctEmployeePOS, SalaryAcctEmployeePO::getId, Collectors.toList());
        List<SalaryAcctResultPO> salaryAcctResultPOS = listBySalaryAcctEmployeeIds(salaryAcctEmployeeIds);
        // 查询人员信息
        List<Long> employeeIds = SalaryEntityUtil.properties(salaryAcctEmployeePOS, SalaryAcctEmployeePO::getEmployeeId, Collectors.toList());
        List<DataCollectionEmployee> simpleEmployees = getSalaryEmployeeService(user).getEmployeeByIdsAll(employeeIds);
        simpleEmployees.forEach(e -> {
            UserStatusEnum userStatusEnum = UserStatusEnum.parseByValue(Integer.parseInt(e.getStatus()));
            if (userStatusEnum != null) {
                e.setStatusName(userStatusEnum.getDefaultLabel());
            }
        });
        // 查询个税扣缴义务人
        Set<Long> taxAgentIds = SalaryEntityUtil.properties(salaryAcctEmployeePOS, SalaryAcctEmployeePO::getTaxAgentId);
        List<TaxAgentPO> taxAgentPOS = getTaxAgentService(user).listByIds(taxAgentIds);
        // 判断是否存在合并计税
        Set<Long> salaryAcctEmployeeIds4ConsolidatedTax;
        if (StringUtils.isNotEmpty(queryParam.getConsolidatedTaxation())) {
            // 如果查询条件中含有"合并计税"，那么在入参中的salaryAcctEmployeePOS就已经全部都是存在合并计税的人员了（前面已经过滤了），无需再次查询合并计税的人员
            salaryAcctEmployeeIds4ConsolidatedTax = SalaryEntityUtil.properties(salaryAcctEmployeePOS, SalaryAcctEmployeePO::getId);
        } else {
            // 如果查询条件中没有包含"合并计税"，那么就需要查询出存在合并计税的人，标记给前端
            SalaryAcctEmployeeQueryParam accEmployeeQueryParam = SalaryAcctEmployeeQueryParam.builder().salaryAcctRecordId(queryParam.getSalaryAcctRecordId()).ids(SalaryEntityUtil.properties(salaryAcctEmployeePOS, SalaryAcctEmployeePO::getId, Collectors.toList())).build();
            List<SalaryAcctEmployeePO> salaryAcctEmployeePOS4ConsolidatedTax = getSalaryAcctEmployeeService(user).listByParam4ConsolidatedTax(accEmployeeQueryParam);
            salaryAcctEmployeeIds4ConsolidatedTax = SalaryEntityUtil.properties(salaryAcctEmployeePOS4ConsolidatedTax, SalaryAcctEmployeePO::getId);
        }
        // 查询公式详情
        Set<Long> formulaIds = SalaryEntityUtil.properties(salarySobItemPOS, SalarySobItemPO::getFormulaId);
        // 获取回算公式
        if (isBackCalc) {
            formulaIds.addAll(salarySobBackItemPOList.stream().map(SalarySobBackItemPO::getFormulaId).collect(Collectors.toList()));
        }
        List<ExpressFormula> expressFormulas = getSalaryFormulaService(user).listExpressFormula(formulaIds);
        Map<Long, String> expressFormulaMap = SalaryEntityUtil.convert2Map(expressFormulas, ExpressFormula::getId, ExpressFormula::getFormula);
        Map<Long, String> customParameters = SalaryEntityUtil.convert2Map(salarySobItemPOS, SalarySobItemPO::getSalaryItemId, salarySobItemPO -> {
            if (salarySobItemPO.getFormulaId() <= 0) {
                return SalaryI18nUtil.getI18nLabel(92004, "输入/导入");
            }
            return expressFormulaMap.getOrDefault(salarySobItemPO.getFormulaId(), StringUtils.EMPTY);
        });
        // 回算公式内容转换
        Map<Long, String> customBackCalcParameters = SalaryEntityUtil.convert2Map(salarySobBackItemPOList, SalarySobBackItemPO::getSalaryItemId, salarySobBackItemPO -> {
            if (salarySobBackItemPO.getFormulaId() <= 0) {
                return SalaryI18nUtil.getI18nLabel(92004, "输入/导入");
            }
            return expressFormulaMap.getOrDefault(salarySobBackItemPO.getFormulaId(), StringUtils.EMPTY);
        });

        // 转换成薪资核算结果列表
        return SalaryAcctResultBO.buildTableData(salaryItemPOS, salarySobEmpFieldPOS, simpleEmployees, salaryAcctEmployeePOS, salaryAcctResultPOS, taxAgentPOS, salaryAcctEmployeeIds4ConsolidatedTax, customParameters, customBackCalcParameters, isBackCalc);

    }

    @Override
    public ConsolidatedTaxDetailDTO getConsolidatedTaxDetail(Long salaryAcctEmployeeId) {
        // 查询当前的薪资核算人员
        SalaryAcctEmployeePO salaryAcctEmployeePO = getSalaryAcctEmployeeService(user).getById(salaryAcctEmployeeId);
        if (Objects.isNull(salaryAcctEmployeePO)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98831, "薪资核算人员不存在或已被删除"));
        }
        // 查询当前的薪资核算人员的个税扣缴义务人
        TaxAgentPO taxAgentPO = getTaxAgentService(user).getById(salaryAcctEmployeePO.getTaxAgentId());
        // 查询当前的薪资核算人员的人员信息
        DataCollectionEmployee simpleEmployee = getSalaryEmployeeService(user).getEmployeeById(salaryAcctEmployeePO.getEmployeeId());
        // 查询当前的薪资核算记录
        SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(salaryAcctEmployeePO.getSalaryAcctRecordId());
        // 查询当前薪资核算记录所用的薪资账套
        SalarySobPO salarySobPO = getSalarySobService(user).getById(salaryAcctRecordPO.getSalarySobId());
        // 当前薪资核算记录所用的薪资账套的员工信息字段
        List<SalarySobEmpFieldPO> salarySobEmpFieldPOS = getSalarySobEmpFieldService(user).listBySalarySobId(salarySobPO.getId());

        List<SalaryAcctRecordPO> salaryAcctRecordPOS = Collections.singletonList(salaryAcctRecordPO);
        List<SalaryAcctEmployeePO> salaryAcctEmployeePOS = Collections.singletonList(salaryAcctEmployeePO);

        // 如果当前薪资核算记录所用的薪资账套的薪资类型是工资薪金，代表可能存在合并计税
        if (Objects.equals(salarySobPO.getIncomeCategory(), IncomeCategoryEnum.WAGES_AND_SALARIES.getValue())) {
            // 查询所有薪资类型为工资薪金的账套
            List<SalarySobPO> salarySobPOS = getSalarySobService(user).listByIncomeCategory(IncomeCategoryEnum.WAGES_AND_SALARIES);
            // 查询相同税款所属期内的薪资类型为工资薪金的账套的所有核算记录
            Set<Long> salarySobIds = SalaryEntityUtil.properties(salarySobPOS, SalarySobPO::getId);
            LocalDateRange taxCycleDateRange = SalaryDateUtil.localDate2Range(salaryAcctRecordPO.getTaxCycle());
            salaryAcctRecordPOS = getSalaryAcctRecordService(user).listBySalarySobIdsAndTaxCycle(salarySobIds, taxCycleDateRange);
            // 查询当前薪资核算人员所涉及的合并计税的所有薪资核算人员
            Set<Long> salaryAcctRecordIds = SalaryEntityUtil.properties(salaryAcctRecordPOS, SalaryAcctRecordPO::getId);
            salaryAcctEmployeePOS = getSalaryAcctEmployeeService(user).listByRecordIdsAndEmpIdAndTaxAgentId(salaryAcctRecordIds, salaryAcctEmployeePO.getEmployeeId(), salaryAcctEmployeePO.getTaxAgentId());
        }
        // 查询薪资核算人员的薪资核算结果
        List<Long> salaryAcctEmployeeIds = SalaryEntityUtil.properties(salaryAcctEmployeePOS, SalaryAcctEmployeePO::getId, Collectors.toList());
        List<SalaryAcctResultPO> salaryAcctResultPOS = listBySalaryAcctEmployeeIds(salaryAcctEmployeeIds);
        // 查询薪资核算人员所有合并计税的薪资核算记录所用的账套
        Set<Long> salarySobIds = SalaryEntityUtil.properties(salaryAcctEmployeePOS, SalaryAcctEmployeePO::getSalarySobId);
        List<SalarySobPO> salarySobPOS = getSalarySobService(user).listByIds(salarySobIds);
        // 查询薪资项目所引用的薪资项目
        List<SalarySobItemPO> salarySobItemPOS = getSalarySobItemService(user).listBySalarySobIds(salarySobIds);
        // 查询薪资项目
        Set<Long> salaryItemIds = SalaryEntityUtil.properties(salarySobItemPOS, SalarySobItemPO::getSalaryItemId);
        List<SalaryItemPO> salaryItemPOS = getSalaryItemService(user).listByIds(salaryItemIds);
        // 转换成合并计税详情dto
        return SalaryAcctResultBO.convert2ConsolidatedTaxDetailDTO(simpleEmployee, taxAgentPO, salarySobEmpFieldPOS, salaryItemPOS, salaryAcctEmployeePOS, salarySobPOS, salaryAcctRecordPOS, salaryAcctResultPOS);
    }

    @Override
    public Map<String, SalaryAcctResultListColumnDTO> getColumnBySalaryAcctRecordId(Long salaryAcctRecordId) {
        SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(salaryAcctRecordId);
        if (Objects.isNull(salaryAcctRecordPO)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98747, "薪资核算记录不存在或已被删除"));
        }
        Map<String, SalaryAcctResultListColumnDTO> resultMap = Maps.newHashMap();
        // 查询薪资核算使用的薪资账套下的薪资项目
        SalarySobItemAggregateDTO salarySobItemAggregateDTO = getSalarySobItemService(user).getAggregateBySalarySobId(salaryAcctRecordPO.getSalarySobId());
        for (SalarySobItemIncomeCategoryDTO incomeCategoryDTO : salarySobItemAggregateDTO.getIncomeCategories()) {
            for (SalarySobItemGroupDTO salarySobItemGroupDTO : incomeCategoryDTO.getItemGroups()) {
                for (SalarySobItemDTO salarySobItemDTO : salarySobItemGroupDTO.getItems()) {
                    SalaryAcctResultListColumnDTO salaryAcctResultListColumnDTO = new SalaryAcctResultListColumnDTO().setColumnName(salarySobItemDTO.getName()).setFormulaId(salarySobItemDTO.getFormulaId()).setFormulaContent(salarySobItemDTO.getFormulaContent()).setShowLock((!Objects.equals(salarySobItemDTO.getFormulaId(), NumberUtils.LONG_ZERO) && salarySobItemDTO.isCanEdit()) || Objects.equals(salarySobItemDTO.getUseInEmployeeSalary(), NumberUtils.INTEGER_ONE));
                    resultMap.put("" + salarySobItemDTO.getSalaryItemId(), salaryAcctResultListColumnDTO);
                }
            }
            for (SalarySobItemDTO salarySobItemDTO : incomeCategoryDTO.getItems()) {
                SalaryAcctResultListColumnDTO salaryAcctResultListColumnDTO = new SalaryAcctResultListColumnDTO().setColumnName(salarySobItemDTO.getName()).setFormulaId(salarySobItemDTO.getFormulaId()).setFormulaContent(salarySobItemDTO.getFormulaContent()).setShowLock((!Objects.equals(salarySobItemDTO.getFormulaId(), NumberUtils.LONG_ZERO) && salarySobItemDTO.isCanEdit()) || Objects.equals(salarySobItemDTO.getUseInEmployeeSalary(), NumberUtils.INTEGER_ONE));
                resultMap.put("" + salarySobItemDTO.getSalaryItemId(), salaryAcctResultListColumnDTO);
            }
        }
        // 查询回算薪资项目
        if (Objects.equals(salaryAcctRecordPO.getBackCalcStatus(), 1)) {
            List<SalarySobItemDTO> backCalcItems = salarySobItemAggregateDTO.getBackCalcItems();
            for (SalarySobItemDTO backCalcItem : backCalcItems) {
                SalaryAcctResultListColumnDTO salaryAcctResultListColumnDTO = new SalaryAcctResultListColumnDTO().setColumnName(backCalcItem.getName()).setFormulaId(backCalcItem.getFormulaId()).setFormulaContent(backCalcItem.getFormulaContent()).setShowLock(true);
                resultMap.put("" + backCalcItem.getSalaryItemId(), salaryAcctResultListColumnDTO);
            }
        }
        return resultMap;
    }

    @Override
    public void save(SalaryAcctResultSaveParam saveParam) {

        List<DataCollectionEmployee> dataCollectionEmployees = getSalaryEmployeeService(user).listAllForReport();
        Map<Long, DataCollectionEmployee> emps = SalaryEntityUtil.convert2Map(dataCollectionEmployees, DataCollectionEmployee::getEmployeeId);

        // 查询薪资核算人员
        SalaryAcctEmployeePO salaryAcctEmployeePO = getSalaryAcctEmployeeService(user).getById(saveParam.getSalaryAcctEmpId());
        if (Objects.isNull(salaryAcctEmployeePO)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98831, "薪资核算人员不存在或已被删除"));
        }
        // 查询原来的薪资核算结果
        List<SalaryAcctResultPO> salaryAcctResultPOSOld = getSalaryAcctResultMapper().listSome(SalaryAcctResultPO.builder().salaryAcctEmpId(saveParam.getSalaryAcctEmpId()).build());
        // 解密
        encryptUtil.decryptList(salaryAcctResultPOSOld, SalaryAcctResultPO.class);
        // 保存参数转换成薪资核算结果po
        List<SalaryAcctResultPO> salaryAcctResultPOS = SalaryAcctResultBO.convert2PO(salaryAcctResultPOSOld, saveParam, salaryAcctEmployeePO, (long) user.getUID());
        SalarySysConfPO autoLock = getSalarySysConfService(user).getOneByCode(SalarySysConstant.EDIT_IMPORT_AUTO_LOCK);
        if (autoLock != null && StringUtils.equals(autoLock.getConfValue(), "1")) {
            // 对比核算结果提取修改了哪些薪资项目
            Set<Long> needLockItems = new HashSet<>();
            Map<Long, SalaryAcctResultPO> oldResutMap = SalaryEntityUtil.convert2Map(salaryAcctResultPOSOld, SalaryAcctResultPO::getSalaryItemId);
            salaryAcctResultPOS.stream().forEach(PO -> {
                String oldValue = Optional.ofNullable(oldResutMap.get(PO.getSalaryItemId())).map(SalaryAcctResultPO::getResultValue).orElse("");
                if (!StringUtils.equals(oldValue, PO.getResultValue())) {
                    needLockItems.add(PO.getSalaryItemId());
                }
            });

            // 编辑的列都自动锁定
            SalaryAcctResultUpdateLockStatusParam updateLockStatusParam = SalaryAcctResultUpdateLockStatusParam.builder().salaryItemIds(needLockItems).salaryAcctRecordId(salaryAcctEmployeePO.getSalaryAcctRecordId()).lockStatus(LockStatusEnum.LOCK).build();
            getSalaryAcctResultService(user).updateLockStatusByParam(updateLockStatusParam);
        }
//        SalaryAcctRecordPO byId = getSalaryAcctRecordService(user).getById(salaryAcctEmployeePO.getSalaryAcctRecordId());
        // 若保存编辑时一同传已发补发，则删除以下代码
//        if(byId.getBackCalcStatus() != null && Objects.equals(byId.getBackCalcStatus(),1)){
//            // 是回算
//            List<Long> salarySobBackItemIds = getSalarySobBackItemService(user).listBySalarySobId(byId.getSalarySobId()).stream().map(SalarySobBackItemPO::getSalaryItemId).collect(Collectors.toList());
//            // 获取已发待发的薪资核算结果
//            List<SalaryAcctResultPO> salarySobBackItemResultPO = salaryAcctResultPOSOld.stream().filter(po -> salarySobBackItemIds.contains(po.getSalaryItemId())).collect(Collectors.toList());
//            salaryAcctResultPOS.addAll(salarySobBackItemResultPO);
//        }
        // 删除原来的薪资核算结果
        List<Long> saveItemIds = saveParam.getItems().stream().map(SalaryAcctResultSaveParam.SalaryAcctResultDetailItemParam::getSalaryItemId).collect(Collectors.toList());
        deleteByAcctEmployeeIdsAndSalaryItemIds(Collections.singletonList(saveParam.getSalaryAcctEmpId()), saveItemIds);
        // 保存薪资核算结果
        if (CollectionUtils.isNotEmpty(salaryAcctResultPOS)) {
            // 加密
            encryptUtil.encryptList(salaryAcctResultPOS, SalaryAcctResultPO.class);
            List<List<SalaryAcctResultPO>> partition = Lists.partition(salaryAcctResultPOS, 100);
            partition.forEach(getSalaryAcctResultMapper()::batchInsert);
        }
        //报表 todo
        getSalaryAcctReportService(user).deleteByAcctEmployeeIdsAndSalaryItemIds(Collections.singletonList(saveParam.getSalaryAcctEmpId()), saveItemIds);
        List<SalaryAcctResultReportPO> salaryAcctResultReportPOS = SalaryAcctResultReportBO.convert2PO(saveParam, salaryAcctEmployeePO, (long) user.getUID(), emps);
        if (CollectionUtils.isNotEmpty(salaryAcctResultReportPOS)) {
            getSalaryAcctReportService(user).batchSave(salaryAcctResultReportPOS);
        }


        // 存储薪资核算结果数据来源日志
        salaryAcctResultPOS = getSalaryAcctRecordService(user).listBySalaryAcctEmpId(saveParam.getSalaryAcctEmpId());
        saveSalaryAcctResultLog(salaryAcctResultPOSOld, salaryAcctResultPOS);

        // 查询操作日志的targetName
        String targetName = getSalaryAcctRecordService(user).getLogTargetNameById(salaryAcctEmployeePO.getSalaryAcctRecordId());
//         查询人员信息
        DataCollectionEmployee simpleEmployee = getSalaryEmployeeService(user).getEmployeeById(salaryAcctEmployeePO.getEmployeeId());
//         查询个税扣缴义务人
        TaxAgentPO taxAgentPO = getTaxAgentService(user).getById(salaryAcctEmployeePO.getTaxAgentId());
//         记录日志
        String operateDesc = simpleEmployee.getUsername() + "(" + Optional.ofNullable(taxAgentPO).map(TaxAgentPO::getName).orElse(StringUtils.EMPTY) + ")";
        LoggerContext<SalaryCheckResultPO> loggerContext = new LoggerContext<>();
        loggerContext.setUser(user);
        loggerContext.setTargetId(String.valueOf(salaryAcctEmployeePO.getSalaryAcctRecordId()));
        loggerContext.setTargetName(targetName);
        loggerContext.setOperateType(OperateTypeEnum.UPDATE.getValue());
        loggerContext.setOperateTypeName(SalaryI18nUtil.getI18nLabel(0, "编辑薪资核算结果") + ": " + operateDesc);
        loggerContext.setOperatedesc(SalaryI18nUtil.getI18nLabel(0, "编辑薪资核算结果") + ": " + operateDesc);
        loggerContext.setNewValueList(Lists.newArrayList(salaryAcctResultPOS));
        SalaryElogConfig.salaryAcctRecordLoggerTemplate.write(loggerContext);
    }

    /**
     * 存储薪资核算结果数据来源日志
     *
     * @param salaryAcctResultPOSOld
     * @param salaryAcctResultPOS
     */
    private void saveSalaryAcctResultLog(List<SalaryAcctResultPO> salaryAcctResultPOSOld, List<SalaryAcctResultPO> salaryAcctResultPOS) {
        // 对比核算结果提取修改了哪些薪资项目
        List<SalaryAcctResultPO> editItems = new ArrayList<>();
        Map<Long, SalaryAcctResultPO> oldResutMap = SalaryEntityUtil.convert2Map(salaryAcctResultPOSOld, SalaryAcctResultPO::getSalaryItemId);
        salaryAcctResultPOS.stream().forEach(PO -> {
            if (oldResutMap.get(PO.getSalaryItemId()) == null) {
                editItems.add(PO);
            } else {
                String oldValue = oldResutMap.get(PO.getSalaryItemId()).getResultValue();
                if (!StringUtils.equals(oldValue, PO.getResultValue())) {
                    editItems.add(PO);
                }
            }
        });
        List<SalaryAcctResultLogPO> needInsertList = SalaryAcctResultLogBO.buildSalaryAcctResultLog(editItems, Long.valueOf(user.getUID()), SalaryAcctResultDataSourceEnum.EDIT);
        getSalaryAcctResultLogService(user).batchInsert(needInsertList);
    }


    @Override
    public void batchSave(List<SalaryAcctResultPO> salaryAcctResultPOS) {
        if (CollectionUtils.isNotEmpty(salaryAcctResultPOS)) {
            // 数据加密
            encryptUtil.encryptList(salaryAcctResultPOS, SalaryAcctResultPO.class);
            List<List<SalaryAcctResultPO>> partition = Lists.partition(salaryAcctResultPOS, 100);
            partition.forEach(getSalaryAcctResultMapper()::batchInsert);

        }
    }


    @Override
    public void deleteBySalaryAcctEmployeeIds(Collection<Long> salaryAcctEmployeeIds) {
        getSalaryAcctResultMapper().deleteBySalaryAcctEmpIds(salaryAcctEmployeeIds);
    }


    @Override
    public void deleteByAcctEmployeeIdsAndSalaryItemIds(List<Long> salaryAcctEmployeeIds, Collection<Long> salaryItemIds) {
        List<List<Long>> partition = Lists.partition(salaryAcctEmployeeIds, 300);
        partition.forEach(part -> getSalaryAcctResultMapper().deleteByAcctEmpIdsAndSalaryItemIds(part, salaryItemIds));
    }

    @Override
    public void deleteBySalaryAcctRecordIds(Collection<Long> salaryAcctRecordIds) {
        getSalaryAcctResultMapper().deleteBySalaryAcctRecordIds(salaryAcctRecordIds);
    }

    @Override
    public void calculate(SalaryAcctCalculateParam calculateParam, DataCollectionEmployee simpleEmployee) {
        StopWatch stopWatch = new StopWatch("总核算耗时，id："+calculateParam.getSalaryAcctRecordId());
        try {
            stopWatch.start("数据准备");
            // 1、查询薪资核算记录
            SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(calculateParam.getSalaryAcctRecordId());
            if (Objects.isNull(salaryAcctRecordPO)) {
                throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98747, "薪资核算记录不存在或已被删除"));
            }
            //查询对应账套
            SalarySobPO salarySobPO = getSalarySobService(user).getById(salaryAcctRecordPO.getSalarySobId());
            if (Objects.isNull(salarySobPO)) {
                throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98747, "薪资账套不存在或已被删除"));
            }
            // 1.1、如果薪资核算记录已经归档了，就不能继续核算
            if (!Objects.equals(salaryAcctRecordPO.getStatus(), SalaryAcctRecordStatusEnum.NOT_ARCHIVED.getValue())) {
                throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(99148, "当前薪资核算记录已归档，请重新打开后再进行核算"));
            }
            // 2、查询薪资核算记录的薪资周期、考勤周期等
            SalarySobCycleDTO salarySobCycleDTO = getSalaryAcctRecordService(user).getSalarySobCycleById(calculateParam.getSalaryAcctRecordId());
            // 3、查询薪资核算记录所用薪资账套的薪资项目副本
            List<SalarySobItemPO> salarySobItemPOS = getSalarySobItemService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId());
            if (CollectionUtils.isEmpty(salarySobItemPOS)) {
                throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(99151, "当前所用的薪资账套未选择任何薪资项目，无法核算"));
            }
            // 回算薪资项目
            List<SalarySobBackItemPO> salarySobBackItems = Collections.emptyList();
            if (Objects.equals(salaryAcctRecordPO.getBackCalcStatus(), 1)) {
                salarySobBackItems = getSalarySobBackItemService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId());
            }
            // 4、查询当前租户的所有薪资项目
            List<SalaryItemPO> salaryItemPOS = getSalaryItemService(user).listAll();
            // 5、查询薪资核算记录所用薪资账套的调薪计薪规则
            List<SalarySobAdjustRulePO> salarySobAdjustRulePOS = getSalarySobAdjustRuleService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId());
            // 6、查询社保福利的所有字段
            Map<String, String> welfareColumns = getSIAccountService(user).welfareColumns();
            // 7、查询考勤引用的所有字段
            List<AttendQuoteFieldListDTO> attendQuoteFieldListDTOS = getAttendQuoteFieldService(user).listAll();

            //核算锁定值
            List<Long> lockSalaryItemIds = salaryAcctRecordPO.getLockSalaryItemIds();
            Map<String, SalaryAcctResultPO> acctResults = new HashMap<>();
            if (CollectionUtils.isNotEmpty(lockSalaryItemIds)) {
                List<SalaryAcctResultPO> acctResultPOS = listBySalaryAcctRecordIdsAndSalaryItemIds(Collections.singleton(calculateParam.getSalaryAcctRecordId()), lockSalaryItemIds);
//                List<SalaryAcctResultPO> acctResultPOS = listBySalaryAcctRecordIds(Collections.singleton(calculateParam.getSalaryAcctRecordId()));
                acctResults = Optional.ofNullable(acctResultPOS).orElse(new ArrayList<>()).stream().filter(po -> lockSalaryItemIds.contains(po.getSalaryItemId())).collect(Collectors.toMap(po -> po.getSalaryItemId() + "_" + po.getSalaryAcctEmpId(), a -> a, (a, b) -> a));
            }

            // 8、查询公式详情
            Set<Long> formulaIds = SalaryEntityUtil.properties(salarySobItemPOS, SalarySobItemPO::getFormulaId);
            formulaIds.addAll(SalaryEntityUtil.properties(salaryItemPOS, SalaryItemPO::getFormulaId));
            formulaIds.addAll(SalaryEntityUtil.properties(salarySobBackItems, SalarySobBackItemPO::getFormulaId));
            List<ExpressFormula> expressFormulas = getSalaryFormulaService(user).listExpressFormula(formulaIds);
            // 本次运算的回算薪资项目所涉及的变量
            Set<String> issuedFieldIds = new HashSet<>();
            // 9、计算薪资项目的运算优先级
            List<List<Long>> salarySobItemsWithPriority = SalaryAcctCalculatePriorityBO.calculatePriority(salarySobItemPOS, salaryItemPOS, expressFormulas, salarySobBackItems, issuedFieldIds);
            // 10、根据id查询其他合并计税的薪资核算记录
            List<SalaryAcctRecordPO> otherSalaryAcctRecordPOS = getSalaryAcctRecordService(user).listById4OtherConsolidatedTax(salaryAcctRecordPO.getId());
            // 11、查询本次核算人员
            List<SalaryAcctEmployeePO> salaryAcctEmployeePOS;
            if (CollectionUtils.isEmpty(calculateParam.getIds())) {
                salaryAcctEmployeePOS = getSalaryAcctEmployeeService(user).listBySalaryAcctRecordId(salaryAcctRecordPO.getId());
            } else {
                salaryAcctEmployeePOS = getSalaryAcctEmployeeService(user).listByIds(calculateParam.getIds());
            }
            if (CollectionUtils.isEmpty(salaryAcctEmployeePOS)) {
                throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(103378, "薪资核算人员不能为空"));
            }

            stopWatch.stop();
            stopWatch.start("核算耗时");

            // 11.1、初始化进度
            ProgressDTO initProgress = new ProgressDTO().setTitle(SalaryI18nUtil.getI18nLabel(97515, "核算中")).setTitleLabelId(97515L).setTotalQuantity(salaryAcctEmployeePOS.size() * 2 + 1).setCalculatedQuantity(0).setProgress(BigDecimal.ZERO).setStatus(true).setMessage(StringUtils.EMPTY);
            getSalaryAcctProgressService(user).initProgress(SalaryCacheKey.ACCT_PROGRESS + calculateParam.getSalaryAcctRecordId(), initProgress);
            // 12、对薪资核算人员进行拆分
            List<List<SalaryAcctEmployeePO>> partition = Lists.partition(salaryAcctEmployeePOS, 100);
            // 12.1、监控子线程的任务执行
            CountDownLatch childMonitor = new CountDownLatch(partition.size());
            // 12.2、记录子线程的执行结果
            BlockingDeque<SalaryAcctCalculateBO.Result> calculateResults = new LinkedBlockingDeque<>(partition.size());
            // 12.3、生成本次运算的key
            String calculateKey = UUID.randomUUID().toString();
            // 12.4、是否采用系统算税
            TaxDeclarationFunctionEnum taxDeclarationFunction = getSalarySysConfService(user).getTaxDeclaration();
            // 12.5、多线程运算，运算结果存放在临时表中
            for (List<SalaryAcctEmployeePO> acctEmployeePOS : partition) {
                SalaryAcctCalculateBO salaryAcctCalculateBO = new SalaryAcctCalculateBO()
                        .setSalaryAcctRecordPO(salaryAcctRecordPO)
                        .setSalarySobPO(salarySobPO)
                        .setSalarySobCycleDTO(salarySobCycleDTO)
                        .setOtherSalaryAcctRecordPOS(otherSalaryAcctRecordPOS)
                        .setSalaryAcctLockResultPOS(MapUtils.emptyIfNull(acctResults))
                        .setLockSalaryItemIds(lockSalaryItemIds)
                        .setSalarySobItemPOS(salarySobItemPOS)
                        .setSalaryItemIdWithPriorityList(salarySobItemsWithPriority)
                        .setExpressFormulas(expressFormulas)
                        .setSalaryItemPOS(salaryItemPOS)
                        .setSalarySobAdjustRulePOS(salarySobAdjustRulePOS)
                        .setWelfareColumns(MapUtils.emptyIfNull(welfareColumns))
                        .setAttendQuoteFieldListDTOS(attendQuoteFieldListDTOS)
                        .setSalaryAcctEmployeePOS(acctEmployeePOS)
                        .setIssuedFieldIds(issuedFieldIds)
                        .setChildMonitor(childMonitor)
                        .setResults(calculateResults)
                        .setCalculateKey(calculateKey)
                        .setTaxDeclarationFunction(taxDeclarationFunction);
                List<SalarySobBackItemPO> finalSalarySobBackItems = salarySobBackItems;
                LocalRunnable localRunnable = new LocalRunnable() {
                    @Override
                    public void execute() {
                        getSalaryAcctCalculateService(user).calculate(salaryAcctCalculateBO, simpleEmployee, finalSalarySobBackItems);
                    }
                };
                ThreadPoolUtil.fixedPoolExecute(ModulePoolEnum.HRM, "salaryAcctCalculate", localRunnable);
            }
            // 13、等待所有子线程执行完毕
            childMonitor.await();

            stopWatch.stop();
            stopWatch.start("数据写入耗时");

            // 14、判断子线程执行结果
            boolean allSuccess = calculateResults.stream().allMatch(SalaryAcctCalculateBO.Result::isStatus);
            if (!allSuccess) {
                // 薪资核算实现的线程的错误信息
                String errorMsg = calculateResults.stream().filter(result -> !result.isStatus()).map(SalaryAcctCalculateBO.Result::getErrMsg).collect(Collectors.joining("|"));
                getSalaryAcctProgressService(user).fail(SalaryCacheKey.ACCT_PROGRESS + calculateParam.getSalaryAcctRecordId(), errorMsg);
                // 删除薪资核算临时存储表中的数据
                getSalaryAcctResultTempService(user).deleteByCalculateKey(calculateKey);
                return;
            }
            // 15、处理核算结果临时表数据
            handleSalaryAcctResultTemp(calculateParam, calculateKey);
            // 16、开始运行校验规则
//            SalaryAcctCheckParam salaryAcctCheckParam = new SalaryAcctCheckParam()
//                    .setSalaryAcctRecordId(calculateParam.getSalaryAcctRecordId())
//                    .setIds(calculateParam.getIds());
//            salaryCheckResultService.check(salaryAcctCheckParam, true, simpleEmployee);
//            Thread.sleep(10);
            getSalaryAcctProgressService(user).finish(SalaryCacheKey.ACCT_PROGRESS + calculateParam.getSalaryAcctRecordId(), true);

            stopWatch.stop();
            log.info(stopWatch.prettyPrint());
            // 存储薪资核算数据来源日志
            new Thread() {
                public void run() {
                    List<Long> exceptItemIds = new ArrayList<>();
                    List<Long> salaryAcctEmployeeIds = salaryAcctEmployeePOS.stream().map(SalaryAcctEmployeePO::getId).collect(Collectors.toList());
                    exceptItemIds.addAll(lockSalaryItemIds);
                    exceptItemIds.addAll(salaryItemPOS.stream().filter(PO -> Objects.equals(PO.getValueType(), NumberUtils.INTEGER_ONE) && Objects.equals(PO.getUseInEmployeeSalary(), 0)).map(SalaryItemPO::getId).collect(Collectors.toList()));
                    getSalaryAcctResultLogService(user).deleteBySalaryAcctEmpIdExceptItemIds(salaryAcctEmployeeIds, exceptItemIds);
                }
            }.start();
            // 记录日志
            // 查询操作日志的targetName
            String targetName = getSalaryAcctRecordService(user).getLogTargetNameById(calculateParam.getSalaryAcctRecordId());
            LoggerContext<SalaryCheckResultPO> loggerContext = new LoggerContext<>();
            loggerContext.setUser(user);
            loggerContext.setTargetId(String.valueOf(calculateParam.getSalaryAcctRecordId()));
            loggerContext.setTargetName(targetName);
            loggerContext.setOperateType(OperateTypeEnum.UPDATE.getValue());
            loggerContext.setOperateTypeName(SalaryI18nUtil.getI18nLabel(0, "薪资核算"));
            loggerContext.setOperatedesc(SalaryI18nUtil.getI18nLabel(0, "薪资核算"));
            SalaryElogConfig.salaryAcctRecordLoggerTemplate.write(loggerContext);
        } catch (Exception e) {
            log.info("薪资核算出错：{}", e.getMessage(), e);
//            throw new SalaryRunTimeException(e);
            getSalaryAcctProgressService(user).fail(SalaryCacheKey.ACCT_PROGRESS + calculateParam.getSalaryAcctRecordId(), SalaryI18nUtil.getI18nLabel(99642, "薪资核算出错") + ": " + e.getMessage());
        } finally {
            // 数据库字段加密用
        }
    }

    /**
     * 处理薪资核算临时存储表中的数据
     *
     * @param calculateParam
     * @param calculateKey
     */
    private void handleSalaryAcctResultTemp(SalaryAcctCalculateParam calculateParam, String calculateKey) {
        StopWatch sw = new StopWatch(calculateParam.getSalaryAcctRecordId() + "");
        // 查询薪资核算结果的临时存储
        sw.start("处理核算数据，查询薪资核算结果的临时存储");
        List<SalaryAcctResultTempPO> salaryAcctResultTempPOS = getSalaryAcctResultTempService(user).listByCalculateKey(calculateKey);
        sw.stop();
        // 删除原来的薪资核算结果
        sw.start("处理核算数据，删除原来的薪资核算结果");
        if (CollectionUtils.isNotEmpty(calculateParam.getIds())) {
            getSalaryAcctResultMapper().deleteBySalaryAcctEmpIds(calculateParam.getIds());
        } else {
            getSalaryAcctResultMapper().deleteBySalaryAcctRecordIds(Collections.singleton(calculateParam.getSalaryAcctRecordId()));
            getSalaryAcctReportService(user).deleteBySalaryAcctRecordId(calculateParam.getSalaryAcctRecordId());
        }
        sw.stop();
        // 保存薪资的薪资核算结果
        sw.start("处理核算数据，保存薪资的薪资核算结果");
        List<SalaryAcctResultPO> salaryAcctResultPOS = SalaryAcctResultBO.convert2ResultPO(salaryAcctResultTempPOS);
        batchSave(salaryAcctResultPOS);
        sw.stop();
        new Thread() {
            public void run() {
                //保存核算报表数据
                List<SalaryAcctResultReportPO> salaryAcctResultReportPOS = SalaryAcctResultReportBO.convert2ReportPO(salaryAcctResultTempPOS, calculateParam.getEmps());
                getSalaryAcctReportService(user).batchSave(salaryAcctResultReportPOS);
            }
        }.start();
        // 删除薪资核算临时存储表中的数据
        sw.start("处理核算数据，删除薪资核算临时存储表中的数据");
        getSalaryAcctResultTempService(user).deleteByCalculateKey(calculateKey);
        sw.stop();
        log.info(sw.prettyPrint());
    }

    @Override
    public List<SalaryAcctResultPO> listBySalaryAcctRecordIdsAndTaxAgentIds(Collection<Long> salaryAcctRecordIds, Collection<Long> taxAgentIds) {
        if (CollectionUtils.isEmpty(salaryAcctRecordIds)) {
            return Collections.emptyList();
        }
        return encryptUtil.decryptList(getSalaryAcctResultMapper().listSome(SalaryAcctResultPO.builder().salaryAcctRecordIds(salaryAcctRecordIds).taxAgentIds(taxAgentIds).build()), SalaryAcctResultPO.class);
    }


//    /**
//     * 查询薪资核算人员
//     *
//     * @param queryParam
//     * @param salaryAcctRecord
//     * @return
//     */
//    private List<SalaryAcctEmployeePO> querySalaryAcctEmployee(SalaryAcctResultListContext salaryAcctResultListContext, SalaryAcctResultQueryParam queryParam, SalaryAcctRecordPO salaryAcctRecord) {
//        // 构建薪资核算人员的查询参数
//        SalaryAcctEmployeeQueryParam salaryAcctEmployeeListQueryParam = new SalaryAcctEmployeeQueryParam();
//        BeanUtils.copyProperties(queryParam, salaryAcctEmployeeListQueryParam);
//        // 查询薪资核算人员
//        List<SalaryAcctEmployeePO> salaryAcctEmployees = getSalaryAcctEmployeeService(user).listByParam(salaryAcctEmployeeListQueryParam);
//        // 查询相同税款所属期内的薪资核算人员
//        List<SalaryAcctEmployeePO> sameTaxCycleSalaryAcctEmployees = salaryAcctEmployeeService
//                .listSameTaxCycle(salaryAcctEmployees, SalaryDateUtil.getFormatYearMonth(salaryAcctRecord.getTaxCycle()));
//        Set<String> sameTaxCycleKeySet = SalaryEntityUtil.properties(sameTaxCycleSalaryAcctEmployees, o -> o.getEmployeeId() + "-" + o.getTaxAgentId());
//        salaryAcctResultListContext.setSameTaxCycleKeySet(sameTaxCycleKeySet);
//        // 过滤合并计税的人员
//        if (StringUtils.isNotEmpty(queryParam.getConsolidatedTaxation())) {
//            salaryAcctEmployees = salaryAcctEmployees.stream()
//                    .filter(salaryAcctEmployeePO -> sameTaxCycleKeySet.contains(salaryAcctEmployeePO.getEmployeeId() + "-" + salaryAcctEmployeePO.getTaxAgentId()))
//                    .collect(Collectors.toList());
//        }
//        // 根据权限过滤
//        return salaryAcctEmployeeService.filterByAuthority(salaryAcctEmployees, employeeId);
//    }

    @Override
    public void updateLockStatusByParam(SalaryAcctResultUpdateLockStatusParam updateParam) {
        // 查询薪资核算记录
        SalaryAcctRecordPO salaryAcctRecord = getSalaryAcctRecordService(user).getById(updateParam.getSalaryAcctRecordId());
        if (CollectionUtils.isEmpty(salaryAcctRecord.getLockSalaryItemIds())) {
            salaryAcctRecord.setLockSalaryItemIds(new ArrayList<>());
        }
        List<SalaryAcctEmployeePO> salaryAcctEmployees = getSalaryAcctEmployeeService(user).listBySalaryAcctRecordId(updateParam.getSalaryAcctRecordId());
        if (CollectionUtils.isEmpty(salaryAcctEmployees)) {
            return;
        }
        // 查询薪资核算结果
//        List<Long> salaryAcctEmployeeIds = SalaryEntityUtil.properties(salaryAcctEmployees, SalaryAcctEmployeePO::getId,Collectors.toList());
//        List<SalaryAcctResultPO> salaryAcctResultValues = this.listBySalaryAcctEmployeeIds(salaryAcctEmployeeIds);
//        Map<Long, SalaryAcctResultPO> salaryAcctResultValueMap = SalaryEntityUtil.convert2Map(salaryAcctResultValues, SalaryAcctResultPO::getSalaryAcctEmpId);
        // 值可以锁定的薪资项目
//        Set<Long> salaryItemIds = canLockSalaryItemIds(salaryAcctRecord.getSalarySobId());
//        if (Objects.nonNull(updateParam.getSalaryItemId())) {
//            if (!salaryItemIds.contains(updateParam.getSalaryItemId())) {
//                return;
//            }
//            salaryItemIds = Collections.singleton(updateParam.getSalaryItemId());
//        }

        Set<Long> salaryItemIds;
        if (CollectionUtils.isNotEmpty(updateParam.getSalaryItemIds())) {
            salaryItemIds = updateParam.getSalaryItemIds();
        } else {
            salaryItemIds = Collections.singleton(updateParam.getSalaryItemId());
        }
        if (updateParam.getLockStatus() == LockStatusEnum.LOCK) {
            Set<Long> lockSalaryItemIds = new HashSet<>();
            lockSalaryItemIds.addAll(salaryAcctRecord.getLockSalaryItemIds());
            lockSalaryItemIds.addAll(salaryItemIds);
            salaryAcctRecord.setLockSalaryItemIds(new ArrayList<Long>(lockSalaryItemIds));
        } else {
            salaryAcctRecord.getLockSalaryItemIds().removeAll(salaryItemIds);
        }
        getSalaryAcctRecordService(user).updateLockSalaryItemIds(salaryAcctRecord);

    }

    @Override
    public void reCalc(Long id) {
        // 无需解密
        SalaryAcctResultPO build = SalaryAcctResultPO.builder().salaryAcctRecordId(id).deleteType(0).build();
        List<SalaryAcctResultPO> salaryAcctResultPOS = getSalaryAcctResultMapper().listSome(build);
        // 回算前的薪资核算结果
        salaryAcctResultPOS.forEach(salaryAcctResult -> salaryAcctResult.setOriginResultValue(salaryAcctResult.getResultValue()));
        // 批量更新
        batchUpdateOriginResultValue(salaryAcctResultPOS);
    }

    @Override
    public List<SalaryAcctResultPO> listBySalaryAcctRecordIdsAndSalaryItemIds(Collection<Long> salaryAcctRecordIds, Collection<Long> salaryItemIds) {
        if (CollectionUtils.isEmpty(salaryAcctRecordIds) || CollectionUtils.isEmpty(salaryItemIds)) {
            return Collections.emptyList();
        }
        List<SalaryAcctResultPO> list = getSalaryAcctResultMapper().listSome(SalaryAcctResultPO.builder().salaryAcctRecordIds(salaryAcctRecordIds).salaryItemIds(salaryItemIds).build());
        return encryptUtil.decryptList(list, SalaryAcctResultPO.class);

    }

    @Override
    public List<SalaryAcctResultPO> listByAcctEmployeeIdsAndSalaryItemIds(List<Long> salaryAcctEmployeeIds, Collection<Long> salaryItemIds) {
        if (CollectionUtils.isEmpty(salaryAcctEmployeeIds)) {
            return Collections.emptyList();
        }
        List<List<Long>> partition = Lists.partition((List<Long>) salaryAcctEmployeeIds, 200);
        List<SalaryAcctResultPO> result = new ArrayList<>();
        partition.forEach(empIds -> {
            SalaryAcctResultPO build = SalaryAcctResultPO.builder().salaryAcctEmpIds(empIds).salaryItemIds(salaryItemIds).build();
            result.addAll(getSalaryAcctResultMapper().listSome(build));
        });

        // 数据解密
        encryptUtil.decryptList(result, SalaryAcctResultPO.class);
        return result;
    }

    @Override
    public void batchUpdateOriginResultValue(List<SalaryAcctResultPO> salaryAcctResultValues) {
        int batchSize = 500;
        List<List<SalaryAcctResultPO>> partition = Lists.partition(salaryAcctResultValues, batchSize);
        for (List<SalaryAcctResultPO> subSalaryAcctResultValues : partition) {
            getSalaryAcctResultMapper().batchUpdateOriginResultValue(subSalaryAcctResultValues);
        }
    }

    @Override
    public List<Long> listAcctEmpIdByAcctEmpId(List<Long> salaryAcctEmployeeIds) {
        if (CollectionUtils.isEmpty(salaryAcctEmployeeIds)) {
            return Collections.emptyList();
        }
        List<List<Long>> partition = Lists.partition((List<Long>) salaryAcctEmployeeIds, 1000);
        List<Long> result = new ArrayList<>();
        partition.forEach(empIds -> {
            result.addAll(getSalaryAcctResultMapper().getAcctEmpIsExist(empIds));
        });
        return result;
    }

    private Set<Long> canLockSalaryItemIds(Long salarySobId) {
        // 值可以锁定的薪资项目
        Set<Long> salaryItemIds = Sets.newHashSet();
        // 查询薪资核算记录所用的薪资账套的薪资项目
        List<SalarySobItemDTO> salarySobItems = Lists.newArrayList();
        SalarySobItemAggregateDTO salarySobItemAggregate = getSalarySobItemService(user).getAggregateBySalarySobId(salarySobId);
        for (SalarySobItemIncomeCategoryDTO incomeCategory : salarySobItemAggregate.getIncomeCategories()) {
            for (SalarySobItemGroupDTO itemGroup : incomeCategory.getItemGroups()) {
                salarySobItems.addAll(itemGroup.getItems());
            }
            salarySobItems.addAll(incomeCategory.getItems());
        }
        for (SalarySobItemDTO salarySobItem : salarySobItems) {
            if (Objects.equals(salarySobItem.getValueType(), SalaryValueTypeEnum.FORMULA.getValue()) && salarySobItem.isCanEdit()) {
                salaryItemIds.add(salarySobItem.getSalaryItemId());
            }
        }
//        for (SalarySobItemDTO backCalcItem : salarySobItemAggregate.getBackCalcItems()) {
//            if (Objects.equals(backCalcItem.getValueType(), SalaryValueTypeEnum.FORMULA.getValue()) && backCalcItem.isCanEdit()) {
//                salaryItemIds.add(backCalcItem.getSalaryItemId());
//            }
//        }
        return salaryItemIds;
    }

    @Override
    public Boolean checkAuth(Long salaryAcctRecordId) {
        // 获取该核算记录的个税扣缴义务
        SalaryAcctRecordPO recordPO = getSalaryAcctRecordService(user).getById(salaryAcctRecordId);
        if (Objects.isNull(recordPO)) {
            return false;
        }
        SalarySobPO salarySobPO = getSalarySobService(user).getById(recordPO.getSalarySobId());
        Long taxAgentId = salarySobPO.getTaxAgentId();
        List<TaxAgentAdminPO> adminTaxAgentList = getTaxAgentAdminService(user).listByEmployeeId(Long.valueOf(user.getUID()));
        Optional<TaxAgentAdminPO> canOperate = adminTaxAgentList.stream().filter(po -> NumberUtils.compare(taxAgentId, po.getTaxAgentId()) == 0).findFirst();
        if (!canOperate.isPresent()) {
            return false;
        }
        return true;
    }

    @Override
    public void batchUpdate(SalaryAcctResultBatchUpdateParam param) {
        ValidUtil.doValidator(param);

        SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(param.getSalaryAcctRecordId());
        if (salaryAcctRecordPO == null) {
            throw new SalaryRunTimeException("薪资核算记录不存在，或已被删除");
        }
        List<SalarySobItemPO> salarySobItemPOS = getSalarySobItemService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId());
        // 薪资账套中包含的薪资项目
        List<Long> salarySobItemIds = salarySobItemPOS.stream().map(SalarySobItemPO::getSalaryItemId).collect(Collectors.toList());
        if (!salarySobItemIds.contains(param.getSalaryItemId())) {
            throw new SalaryRunTimeException("该账套不包含该薪资项目或已被删除，请先检查账套");
        }
        // 获取需要更新的核算人员信息
        List<SalaryAcctEmployeePO> salaryAcctEmployeePOList = new ArrayList<>();
        if (CollectionUtils.isEmpty(param.getIdList())) {
            // 没有选择核算人员，更新核算记录中所有人员
            salaryAcctEmployeePOList.addAll(getSalaryAcctEmployeeService(user).listBySalaryAcctRecordId(salaryAcctRecordPO.getId()));
        } else {
            salaryAcctEmployeePOList.addAll(getSalaryAcctEmployeeService(user).listByIds(param.getIdList()));
        }

        if (CollectionUtils.isNotEmpty(salaryAcctEmployeePOList)) {
            List<Long> salaryAcctEmployeeIdList = SalaryEntityUtil.properties(salaryAcctEmployeePOList, SalaryAcctEmployeePO::getId, Collectors.toList());
            // 查询薪资核算结果
            List<SalaryAcctResultPO> resultPOS = listByAcctEmployeeIdsAndSalaryItemIds(salaryAcctEmployeeIdList, Collections.singleton(param.getSalaryItemId()));
            Map<Long, SalaryAcctResultPO> salaryAcctResultPOMap = SalaryEntityUtil.convert2Map(resultPOS, SalaryAcctResultPO::getSalaryAcctEmpId);
            List<DataCollectionEmployee> dataCollectionEmployees = getSalaryEmployeeService(user).listAllForReport();
            Map<Long, DataCollectionEmployee> emps = SalaryEntityUtil.convert2Map(dataCollectionEmployees, DataCollectionEmployee::getEmployeeId);
            List<SalaryAcctResultPO> needUpdateList = new ArrayList<>();
            List<SalaryAcctResultPO> needInsertList = new ArrayList<>();
            Date now = new Date();
            List<SalaryAcctResultReportPO> salaryAcctResultReportPOS = new ArrayList<>();
            salaryAcctEmployeePOList.forEach(salaryAcctEmployeePO -> {
                if (salaryAcctResultPOMap.containsKey(salaryAcctEmployeePO.getId())) {
                    // 更新
                    SalaryAcctResultPO po = salaryAcctResultPOMap.get(salaryAcctEmployeePO.getId());
                    po.setResultValue(param.getValue());
                    po.setUpdateTime(now);
                    needUpdateList.add(po);
                } else {
                    // 新增
                    needInsertList.add(SalaryAcctResultPO.builder()
                            .salarySobId(salaryAcctRecordPO.getSalarySobId())
                            .salaryItemId(param.getSalaryItemId())
                            .salaryAcctRecordId(param.getSalaryAcctRecordId())
                            .salaryAcctEmpId(salaryAcctEmployeePO.getId())
                            .employeeId(salaryAcctEmployeePO.getEmployeeId())
                            .taxAgentId(salaryAcctEmployeePO.getTaxAgentId())
                            .resultValue(param.getValue())
                            .originResultValue("")
                            .creator(Long.valueOf(user.getUID()))
                            .createTime(now)
                            .updateTime(now)
                            .deleteType(NumberUtils.INTEGER_ZERO)
                            .tenantKey(SalaryDefaultTenantConstant.DEFAULT_TENANT_KEY)
                            .build());
                }

                // 报表
                SalaryAcctResultReportPO po = SalaryAcctResultReportPO.builder()
                        .id(IdGenerator.generate())
                        .salarySobId(salaryAcctRecordPO.getSalarySobId())
                        .salaryItemId(param.getSalaryItemId())
                        .salaryAcctRecordId(param.getSalaryAcctRecordId())
                        .salaryAcctEmpId(salaryAcctEmployeePO.getId().toString())
                        .employeeId(salaryAcctEmployeePO.getEmployeeId().toString())
                        .taxAgentId(salaryAcctEmployeePO.getTaxAgentId())
                        .resultValue(param.getValue())
                        .creator(Long.valueOf(user.getUID()))
                        .createTime(now)
                        .updateTime(now)
                        .deleteType(NumberUtils.INTEGER_ZERO)
                        .tenantKey(SalaryDefaultTenantConstant.DEFAULT_TENANT_KEY)
                        .build();
                DataCollectionEmployee dataCollectionEmployee = emps.get(salaryAcctEmployeePO.getEmployeeId());
                if (dataCollectionEmployee != null) {
                    po.setDepartmentId(dataCollectionEmployee.getDepartmentId());
                    po.setSubcompanyId(dataCollectionEmployee.getSubcompanyid());
                    po.setCostcenterId(dataCollectionEmployee.getCostcenterId());
                    po.setJobtitleId(dataCollectionEmployee.getJobtitleId());
                    po.setLocationId(dataCollectionEmployee.getLocationId());
                }
                salaryAcctResultReportPOS.add(po);
            });

            // 入库
            if (CollectionUtils.isNotEmpty(needUpdateList)) {
                // 数据加密
                encryptUtil.encryptList(needUpdateList, SalaryAcctResultPO.class);
                List<List<SalaryAcctResultPO>> partition = Lists.partition(needUpdateList, 100);
                partition.forEach(getSalaryAcctResultMapper()::batchUpdate);
            }
            batchSave(needInsertList);

            // 报表入库前先删除
            getSalaryAcctReportService(user).deleteByAcctEmployeeIdsAndSalaryItemIds(param.getIdList(), Collections.singletonList(param.getSalaryItemId()));
            getSalaryAcctReportService(user).batchSave(salaryAcctResultReportPOS);

        }
    }
}