package com.engine.salary.service.impl;

import cn.hutool.core.date.DateUtil;
import com.api.browser.bean.SearchConditionGroup;
import com.api.browser.bean.SearchConditionItem;
import com.api.browser.util.ConditionFactory;
import com.api.browser.util.ConditionType;
import com.api.formmode.mybatis.util.SqlProxyHandle;
import com.cloudstore.dev.api.util.Util_DataCache;
import com.engine.common.util.ServiceUtil;
import com.engine.core.impl.Service;
import com.engine.salary.biz.AddUpDeductionBiz;
import com.engine.salary.common.LocalDateRange;
import com.engine.salary.config.SalaryElogConfig;
import com.engine.hrmelog.entity.dto.LoggerContext;
import com.engine.salary.encrypt.EncryptUtil;
import com.engine.salary.entity.datacollection.AddUpDeduction;
import com.engine.salary.entity.datacollection.DataCollectionEmployee;
import com.engine.salary.entity.datacollection.dto.AddUpDeductionDTO;
import com.engine.salary.entity.datacollection.dto.AddUpDeductionRecordDTO;
import com.engine.salary.entity.datacollection.param.AddUpDeductionImportParam;
import com.engine.salary.entity.datacollection.param.AddUpDeductionQueryParam;
import com.engine.salary.entity.datacollection.param.AddUpDeductionRecordDeleteParam;
import com.engine.salary.entity.datacollection.param.AddUpDeductionRecordParam;
import com.engine.salary.entity.datacollection.po.SpecialAddDeductionPO;
import com.engine.salary.entity.salaryacct.po.SalaryAcctEmployeePO;
import com.engine.salary.entity.salaryacct.po.SalaryAcctRecordPO;
import com.engine.salary.entity.taxagent.bo.TaxAgentBO;
import com.engine.salary.entity.taxagent.dto.TaxAgentEmployeeDTO;
import com.engine.salary.entity.taxagent.dto.TaxAgentManageRangeEmployeeDTO;
import com.engine.salary.entity.taxagent.po.TaxAgentPO;
import com.engine.salary.enums.OperateTypeEnum;
import com.engine.salary.enums.UserStatusEnum;
import com.engine.salary.enums.salaryaccounting.SalaryAcctRecordStatusEnum;
import com.engine.salary.exception.SalaryRunTimeException;
import com.engine.salary.mapper.datacollection.AddUpDeductionMapper;
import com.engine.salary.mapper.sys.SalarySysConfMapper;
import com.engine.salary.service.*;
import com.engine.salary.sys.entity.po.SalarySysConfPO;
import com.engine.salary.sys.entity.vo.OrderRuleVO;
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.MapperProxyFactory;
import com.engine.salary.util.excel.ExcelComment;
import com.engine.salary.util.excel.ExcelParseHelper;
import com.engine.salary.util.excel.ExcelSupport;
import com.engine.salary.util.excel.ExcelUtil;
import com.engine.salary.util.page.PageInfo;
import com.engine.salary.util.page.SalaryPageUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import weaver.file.ImageFileManager;
import weaver.general.Util;
import weaver.hrm.User;

import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.time.*;
import java.util.*;
import java.util.stream.Collectors;

import static com.engine.salary.constant.SalaryDefaultTenantConstant.DEFAULT_TENANT_KEY;
import static com.engine.salary.util.excel.ExcelSupport.EXCEL_TYPE_XLSX;

/**
 * 累计专项
 * <p>Copyright: Copyright (c) 2022</p>
 * <p>Company: 泛微软件</p>
 *
 * @author qiantao
 * @version 1.0
 **/
public class AddUpDeductionServiceImpl extends Service implements AddUpDeductionService {
    private EncryptUtil encryptUtil = new EncryptUtil();

    private AddUpDeductionMapper getAddUpDeductionMapper() {
        return MapperProxyFactory.getProxy(AddUpDeductionMapper.class);
    }

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

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

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

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

    private SalarySysConfMapper getSalarySysConfMapper() {
        return SqlProxyHandle.getProxy(SalarySysConfMapper.class);
    }

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

    private SpecialAddDeductionService getSpecialAddDeductionService(User user) {
        return ServiceUtil.getService(SpecialAddDeductionServiceImpl.class, user);
    }

    @Override
    public Map<String, Object> getSearchCondition(Map<String, Object> params) {
        Map<String, Object> apidatas = new HashMap<String, Object>();
        ConditionFactory conditionFactory = new ConditionFactory(user);

        //条件组
        List<SearchConditionGroup> addGroups = new ArrayList<SearchConditionGroup>();

        List<SearchConditionItem> conditionItems = new ArrayList<SearchConditionItem>();

        //文本输入框
        SearchConditionItem username = conditionFactory.createCondition(ConditionType.INPUT, 25034, "username");
        username.setInputType("input");
        username.setColSpan(2);
        username.setFieldcol(16);
        username.setLabelcol(8);
        username.setViewAttr(2);
        username.setLabel("姓名");
        conditionItems.add(username);


        SearchConditionItem departmentName = conditionFactory.createCondition(ConditionType.BROWSER, 502227, "departmentIds", "57");
        departmentName.setInputType("browser");
        departmentName.setColSpan(2);
        departmentName.setFieldcol(16);
        departmentName.setLabelcol(8);
        departmentName.setViewAttr(2);
        departmentName.setIsQuickSearch(false);
        departmentName.setLabel("部门");
        conditionItems.add(departmentName);


        SearchConditionItem jobNum = conditionFactory.createCondition(ConditionType.INPUT, 25034, "jobNum");
        jobNum.setInputType("input");
        jobNum.setColSpan(2);
        jobNum.setFieldcol(16);
        jobNum.setLabelcol(8);
        jobNum.setViewAttr(2);
        jobNum.setLabel("工号");
        conditionItems.add(jobNum);

        addGroups.add(new SearchConditionGroup("常用条件", true, conditionItems));

        apidatas.put("condition", addGroups);
        return apidatas;
    }


    @Override
    public Map<String, Object> importAddUpDeduction(AddUpDeductionImportParam importParam) {
        long currentEmployeeId = user.getUID();
        Boolean openDevolution = getTaxAgentService(user).isOpenDevolution();

        Map<String, Object> apidatas = new HashMap<String, Object>();
        AddUpDeductionBiz addUpDeductionBiz = new AddUpDeductionBiz();

        //检验参数
        checkImportParam(importParam);

        //税款所属期
        String declareMonthStr = importParam.getDeclareMonth();
        //excel文件id
        String imageId = Util.null2String(importParam.getImageId());
        Validate.notBlank(imageId, "imageId为空");
        // 获取所有个税扣缴义务人
        Collection<TaxAgentManageRangeEmployeeDTO> taxAgentList = getTaxAgentService(user).listTaxAgentAndEmployeeTree(currentEmployeeId);
        //个税扣缴义务人
        String taxAgentId = Util.null2String(importParam.getTaxAgentId());
        // 获取租户下所有的人员
        List<DataCollectionEmployee> employees = getSalaryEmployeeService(user).listEmployee();
        // 已经核算过的不可操作
        // 获取已经核算的数据
        List<SalaryAcctEmployeePO> salaryAcctEmployees = getAccountedEmployeeData(declareMonthStr);
        // 查询已有数据
        Date declareMonth = SalaryDateUtil.localDateToDate(LocalDate.parse(declareMonthStr + "-01", SalaryDateUtil.DATE_FORMATTER));
        List<AddUpDeduction> list = getAddUpDeductionMapper().listSome(AddUpDeduction.builder().declareMonth(declareMonth).build());

        //查询对于人员信息导入筛选的全局配置
        SalarySysConfPO salarySysConfPO = getSalarySysConfMapper().getOneByCode("matchEmployeeMode");
        String confValue = (salarySysConfPO != null && salarySysConfPO.getConfValue() != null && !"".equals(salarySysConfPO.getConfValue())) ? salarySysConfPO.getConfValue() : "0";

        InputStream fileInputStream = null;
        try {
            fileInputStream = ImageFileManager.getInputStreamById(Integer.parseInt(imageId));
            List<AddUpDeductionDTO> addUpDeductions = ExcelParseHelper.parse2Map(fileInputStream, AddUpDeductionDTO.class, 0, 1, 14, "addUpDeductionTemplate.xlsx");

            int total = addUpDeductions.size();
            int index = 0;
            int successCount = 0;
            int errorCount = 0;

            // 错误excel内容
            List<Map> errorData = new ArrayList<>();
            //合规数据
            List<AddUpDeduction> eligibleData = new ArrayList<>();

            List<TaxAgentManageRangeEmployeeDTO.TaxAgentEmployee> taxAgentEmployees = Lists.newArrayList();
            for (int i = 0; i < addUpDeductions.size(); i++) {

                AddUpDeductionDTO dto = addUpDeductions.get(i);

                Date now = new Date();
                //待插入数据库对象
                AddUpDeduction addUpDeduction = AddUpDeduction.builder()
                        .tenantKey(DEFAULT_TENANT_KEY)
                        .createTime(now)
                        .updateTime(now)
                        .creator((long) user.getUID())
                        .declareMonth(declareMonth).build();

                //异常点数量
                int errorSum = 0;

                //行号
                String rowIndex = String.format("第%s行", i + 2);

                //相同的姓名
                String userName = dto.getUsername();
                String deparmentName = dto.getDepartmentName();
                String mobile = dto.getMobile();
                String workcode = dto.getJobNum();
                String idNo = dto.getIdNo();

                //筛选导入人员信息可以在人力资源池中匹配到的人员信息
                List<DataCollectionEmployee> employeeSameIds = getSalaryEmployeeService(user).matchImportEmployee(confValue, employees, userName, deparmentName, mobile, workcode, idNo, null);

                if (StringUtils.isBlank(userName) && "0".equals(confValue)) {
                    //姓名 不能为空
                    //错误消息对象
                    Map<String, String> errorMessageMap = Maps.newHashMap();
                    errorMessageMap.put("message", rowIndex + "姓名不能为空");
                    errorData.add(errorMessageMap);
                    errorSum += 1;
                } else if (CollectionUtils.isEmpty(employeeSameIds)) {
                    Map<String, String> errorMessageMap = Maps.newHashMap();
                    errorMessageMap.put("message", rowIndex + "员工信息不存在");
                    errorData.add(errorMessageMap);
                    errorSum += 1;
                } else if (employeeSameIds.size() > 1) {
                    //存在离职和在职状态取在职状态
                    employeeSameIds = employeeSameIds.stream()
                            .filter(e -> UserStatusEnum.getNormalStatus().contains(e.getStatus()))
                            .collect(Collectors.toList());
                    if (employeeSameIds.size() != 1) {
                        Map<String, String> errorMessageMap = Maps.newHashMap();
                        errorMessageMap.put("message", rowIndex + "员工信息不存在或者存在多个员工");
                        errorData.add(errorMessageMap);
                        errorSum += 1;
                    } else {
                        Long employeeId = CollectionUtils.isNotEmpty(employeeSameIds) && employeeSameIds.size() == 1 ? employeeSameIds.get(0).getEmployeeId() : null;
                        addUpDeduction.setEmployeeId(employeeId);
                    }
                } else {
                    Long employeeId = CollectionUtils.isNotEmpty(employeeSameIds) && employeeSameIds.size() == 1 ? employeeSameIds.get(0).getEmployeeId() : null;
                    if (employeeId != null && employeeId > 0) {
                        addUpDeduction.setEmployeeId(employeeId);
                    } else {
                        //姓名错误，系统内不存在该姓名
                        Map<String, String> errorMessageMap = Maps.newHashMap();
                        errorMessageMap.put("message", rowIndex + "姓名错误，系统内不存在该姓名");
                        errorData.add(errorMessageMap);
                        errorSum += 1;
                    }
                }


                String taxAgentName = dto.getTaxAgentName();
                if (StringUtils.isBlank(taxAgentName)) {
                    //个税扣缴义务人不能为空
                    Map<String, String> errorMessageMap = Maps.newHashMap();
                    errorMessageMap.put("message", rowIndex + "个税扣缴义务人不能为空");
                    errorData.add(errorMessageMap);
                    errorSum += 1;
                } else {
                    Optional<TaxAgentManageRangeEmployeeDTO> optionalTemp = taxAgentList.stream().filter(m -> m.getTaxAgentName().equals(taxAgentName)).findFirst();
                    if (optionalTemp.isPresent()) {
                        if (StringUtils.isNotEmpty(taxAgentId) && !optionalTemp.get().getTaxAgentId().equals(Long.valueOf(taxAgentId))) {
                            //个税扣缴义务人与导入时选择的不一致
                            Map<String, String> errorMessageMap = Maps.newHashMap();
                            errorMessageMap.put("message", rowIndex + "个税扣缴义务人与导入时选择的不一致");
                            errorData.add(errorMessageMap);
                            errorSum += 1;
                        } else {
                            addUpDeduction.setTaxAgentId(optionalTemp.get().getTaxAgentId());
                            taxAgentEmployees = optionalTemp.get().getEmployeeList();
                        }
                    } else {
                        //个税扣缴义务人不存在
                        Map<String, String> errorMessageMap = Maps.newHashMap();
                        errorMessageMap.put("message", rowIndex + "个税扣缴义务人不存在或不在权限范围内");
                        errorData.add(errorMessageMap);
                        errorSum += 1;
                    }
                }


                //fixme 分权判断
//                if (openDevolution) {
//                    Optional<TaxAgentManageRangeEmployeeDTO.TaxAgentEmployee> optionalTaxAgentEmp = taxAgentEmployees.stream().filter(f -> f.getEmployeeId().equals(addUpDeduction.getEmployeeId())).findFirst();
//                    if (!optionalTaxAgentEmp.isPresent()) {
//                        Map<String, String> errorMessageMap = Maps.newHashMap();
//                        errorMessageMap.put("message", rowIndex + "该条数据不在个税扣缴义务人人员范围内，不可导入");
//                        errorData.add(errorMessageMap);
//                        errorSum += 1;
//                    }
//                }

                // 判断是否有核算过
                if (CollectionUtils.isNotEmpty(salaryAcctEmployees)) {
                    Optional<SalaryAcctEmployeePO> optionalAcctEmp = salaryAcctEmployees.stream().filter(f -> f.getEmployeeId().equals(addUpDeduction.getEmployeeId()) && f.getTaxAgentId().equals(addUpDeduction.getTaxAgentId())).findFirst();
                    boolean isExist = list.stream().anyMatch(f -> f.getEmployeeId().equals(addUpDeduction.getEmployeeId()) && f.getTaxAgentId().equals(addUpDeduction.getTaxAgentId()));
                    if (optionalAcctEmp.isPresent() && isExist) {
                        Map<String, String> errorMessageMap = Maps.newHashMap();
                        errorMessageMap.put("message", rowIndex + "该年月这条数据已经核算过，不可导入");
                        errorData.add(errorMessageMap);
                        errorSum += 1;
                    }
                }
                //累计子女教育
                String addUpChildEducation = dto.getAddUpChildEducation();
                addUpDeduction.setAddUpChildEducation(Util.null2String(addUpChildEducation));
                //累计继续教育
                String addUpContinuingEducation = dto.getAddUpContinuingEducation();
                addUpDeduction.setAddUpContinuingEducation(Util.null2String(addUpContinuingEducation));
                //累计住房贷款利息
                String addUpHousingLoanInterest = dto.getAddUpHousingLoanInterest();
                addUpDeduction.setAddUpHousingLoanInterest(Util.null2String(addUpHousingLoanInterest));
                //累计住房租金
                String addUpHousingRent = dto.getAddUpHousingRent();
                addUpDeduction.setAddUpHousingRent(Util.null2String(addUpHousingRent));
                //累计赡养老人
                String addUpSupportElderly = dto.getAddUpSupportElderly();
                addUpDeduction.setAddUpSupportElderly(Util.null2String(addUpSupportElderly));
                addUpDeduction.setAddUpIllnessMedical(Util.null2String(dto.getAddUpIllnessMedical()));
                addUpDeduction.setAddUpInfantCare(Util.null2String(dto.getAddUpInfantCare()));
                if (errorSum == 0) {
                    successCount += 1;
                    // 合格数据
                    eligibleData.add(addUpDeduction);
                } else {
                    errorCount += 1;
                    // 添加错误数据
                }
            }
            //入库
            handleImportData(eligibleData);
            apidatas.put("successCount", successCount);
            apidatas.put("errorCount", errorCount);
            apidatas.put("errorData", errorData);
        } finally {
            IOUtils.closeQuietly(fileInputStream);
        }
        return apidatas;
    }

    public void handleImportData(List<AddUpDeduction> pos) {
        if (CollectionUtils.isEmpty(pos)) {
            return;
        }
        AddUpDeductionBiz addUpDeductionBiz = new AddUpDeductionBiz();
        AddUpDeduction po = pos.get(0);
        // 多条相同人的则以第一条为准，如果逆序排列（用于重复的则以最后一条为准）Collections.reverse(pos);
        // 去重（通过记录的唯一条件(申报月份，人员id，个税扣缴义务人id)拼接）
        List<AddUpDeduction> finalPos = pos.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(f -> f.getEmployeeId() + "-" + f.getTaxAgentId()))), ArrayList::new));
        // 查询已有数据
        List<AddUpDeduction> list = addUpDeductionBiz.listSome(AddUpDeduction.builder().declareMonth(po.getDeclareMonth()).build());
        // 待修改的 本地已存在则更新【交集】
        List<AddUpDeduction> updateList = list.stream().map(m -> {
            Optional<AddUpDeduction> optional = finalPos.stream().filter(p -> (p.getEmployeeId() + "-" + p.getTaxAgentId()).equals(m.getEmployeeId() + "-" + m.getTaxAgentId())).findFirst();
            AddUpDeduction temp = null;
            if (optional.isPresent()) {
                temp = optional.get();
                // 换成本地库的id
                temp.setId(m.getId());
            }
            return temp;
        }).filter(Objects::nonNull).collect(Collectors.toList());
        // 待新增的 导入比本地多，则新增【差集(导入 - local)】
        List<AddUpDeduction> saveList = finalPos.stream().map(m -> {
            Optional<AddUpDeduction> optional = list.stream().filter(p -> (p.getEmployeeId() + "-" + p.getTaxAgentId()).equals(m.getEmployeeId() + "-" + m.getTaxAgentId())).findFirst();
            AddUpDeduction temp = null;
            if (!optional.isPresent()) {
                temp = m;
            }
            return temp;
        }).filter(Objects::nonNull).collect(Collectors.toList());

        // 修改
        if (CollectionUtils.isNotEmpty(updateList)) {
            addUpDeductionBiz.batchUpdate(updateList);
        }
        // 保存
        if (CollectionUtils.isNotEmpty(saveList)) {
            addUpDeductionBiz.batchSave(saveList);
        }

        // 记录操作日志
        // 根据月份、人员id查出保存的数据
        List<Long> empIds = saveList.stream().map(AddUpDeduction::getEmployeeId).collect(Collectors.toList());
        List<AddUpDeduction> insertList = addUpDeductionBiz.listSome(AddUpDeduction.builder().declareMonth(po.getDeclareMonth()).employeeIds(empIds).build());
        Map<String, AddUpDeduction> insertMap = SalaryEntityUtil.convert2Map(insertList, p -> p.getTaxAgentId() + "-" + SalaryDateUtil.getFormatYearMonth(p.getDeclareMonth()) + "-" + p.getEmployeeId());
        saveList.forEach(save -> {
            AddUpDeduction addUpDeduction = insertMap.get(save.getTaxAgentId() + "-" + SalaryDateUtil.getFormatYearMonth(save.getDeclareMonth()) + "-" + save.getEmployeeId());
            if (addUpDeduction != null) {
                updateList.add(addUpDeduction);
            }
        });

        if (CollectionUtils.isNotEmpty(updateList)) {
            String formatYearMonth = SalaryDateUtil.getFormatYearMonth(po.getDeclareMonth());
            LoggerContext loggerContext = new LoggerContext();
            loggerContext.setUser(user);
            loggerContext.setTargetName(SalaryI18nUtil.getI18nLabel(0, "累计专项附加扣除 ") + formatYearMonth);
            loggerContext.setOperateType(OperateTypeEnum.ADD.getValue());
            loggerContext.setOperateTypeName(SalaryI18nUtil.getI18nLabel(0, "导入"));
            loggerContext.setOperatedesc(SalaryI18nUtil.getI18nLabel(0, "导入累计专项附加扣除 ") + formatYearMonth);
            loggerContext.setNewValueList(updateList);
            SalaryElogConfig.addUpDeductionLoggerTemplate.write(loggerContext);
        }

    }

    @Override
    public void editAddUpDeduction(AddUpDeductionRecordParam addUpDeduction) {
        AddUpDeductionBiz addUpDeductionBiz = new AddUpDeductionBiz();
        String declareMonthStr = addUpDeduction.getDeclareMonth();

        Long currentEmployeeId = (long) user.getUID();
        // 获取所有个税扣缴义务人
        Collection<TaxAgentManageRangeEmployeeDTO> taxAgentList = getTaxAgentService(user).listTaxAgentAndEmployeeTree(currentEmployeeId);
        AddUpDeduction byId = addUpDeductionBiz.getById(addUpDeduction.getId());
        if (byId == null) {
            throw new SalaryRunTimeException("该数据不存在！");
        }
        Long taxAgentId = byId.getTaxAgentId();
        boolean canEdit = taxAgentList.stream().anyMatch(t -> Objects.equals(t.getTaxAgentId(), taxAgentId));
        if (!canEdit) {
            //没有编辑权限
            throw new SalaryRunTimeException("该个税扣缴义务人无权限编辑此数据！");
        }

        // 已经核算过的不可操作
        // 获取已经核算的数据
        List<SalaryAcctEmployeePO> salaryAcctEmployees = getAccountedEmployeeData(declareMonthStr);
        // 判断是否有核算过
        if (CollectionUtils.isNotEmpty(salaryAcctEmployees)) {
            Optional<SalaryAcctEmployeePO> optionalAcctEmp = salaryAcctEmployees.stream().filter(f -> f.getEmployeeId().equals(addUpDeduction.getEmployeeId()) && f.getTaxAgentId().equals(addUpDeduction.getTaxAgentId())).findFirst();
            if (optionalAcctEmp.isPresent()) {
                throw new SalaryRunTimeException("该年月这条数据已经核算过,不可进行编辑！");
            }
        }
        ArrayList<AddUpDeduction> updateList = new ArrayList<>();
        AddUpDeduction build = AddUpDeduction.builder().id(addUpDeduction.getId()).addUpChildEducation(addUpDeduction.getAddUpChildEducation()).addUpContinuingEducation(addUpDeduction.getAddUpContinuingEducation()).addUpHousingLoanInterest(addUpDeduction.getAddUpHousingLoanInterest()).addUpHousingRent(addUpDeduction.getAddUpHousingRent()).addUpSupportElderly(addUpDeduction.getAddUpSupportElderly()).addUpIllnessMedical(addUpDeduction.getAddUpIllnessMedical()).addUpInfantCare(addUpDeduction.getAddUpInfantCare()).createTime(new Date()).build();
        updateList.add(build);
        addUpDeductionBiz.batchUpdate(updateList);

        // 记录日志
        AddUpDeduction newValue = addUpDeductionBiz.getById(build.getId());
        String name = SalaryI18nUtil.getI18nLabel(0, "编辑");
        LoggerContext<AddUpDeduction> loggerContext = new LoggerContext<>();
        loggerContext.setTargetId(addUpDeduction.getId().toString());
        loggerContext.setTargetName(SalaryI18nUtil.getI18nLabel(0, "累计专项附加扣除") + "-" + addUpDeduction.getId());
        loggerContext.setOperateType(OperateTypeEnum.UPDATE.getValue());
        loggerContext.setOperateTypeName(name);
        loggerContext.setOperatedesc(SalaryI18nUtil.getI18nLabel(0, "累计专项附加扣除") + "-" + SalaryI18nUtil
                .getI18nLabel(0, "编辑"));
        loggerContext.setOldValues(byId);
        loggerContext.setNewValues(newValue);
        loggerContext.setUser(user);
        SalaryElogConfig.addUpDeductionLoggerTemplate.write(loggerContext);
    }

    @Override
    public void createAddUpDeduction(AddUpDeductionRecordParam addUpDeductionRecordParam) {
        long currentEmployeeId = user.getUID();
        Boolean openDevolution = getTaxAgentService(user).isOpenDevolution();
        AddUpDeductionBiz addUpDeductionBiz = new AddUpDeductionBiz();

        //税款所属期
        String declareMonthStr = addUpDeductionRecordParam.getDeclareMonth();
        if (declareMonthStr.equals("")) {
            throw new SalaryRunTimeException("税款所属期不能为空！");
        }
        // 获取所有个税扣缴义务人
        Collection<TaxAgentManageRangeEmployeeDTO> taxAgentList = getTaxAgentService(user).listTaxAgentAndEmployeeTree(currentEmployeeId);
        //个税扣缴义务人
        String taxAgentId = Util.null2String(addUpDeductionRecordParam.getTaxAgentId());
        // 获取租户下所有的人员
        List<DataCollectionEmployee> employees = getSalaryEmployeeService(user).listEmployee();
        // 已经核算过的不可操作
        // 获取已经核算的数据
        List<SalaryAcctEmployeePO> salaryAcctEmployees = getAccountedEmployeeData(declareMonthStr);
        // 查询已有数据
        Date declareMonth = SalaryDateUtil.localDateToDate(LocalDate.parse(declareMonthStr + "-01", SalaryDateUtil.DATE_FORMATTER));
        List<AddUpDeduction> list = getAddUpDeductionMapper().listSome(AddUpDeduction.builder().declareMonth(declareMonth).build());

        //查询对于人员信息导入筛选的全局配置
        SalarySysConfPO salarySysConfPO = getSalarySysConfMapper().getOneByCode("matchEmployeeMode");
        List<AddUpDeduction> insertData = new ArrayList<>();
        List<TaxAgentManageRangeEmployeeDTO.TaxAgentEmployee> taxAgentEmployees = Lists.newArrayList();
        Date now = new Date();
        //待插入数据库对象
        AddUpDeduction addUpDeduction = AddUpDeduction.builder()
                .tenantKey(DEFAULT_TENANT_KEY)
                .createTime(now)
                .updateTime(now)
                .creator((long) user.getUID())
                .declareMonth(declareMonth).build();
        boolean employeeSameId = employees.stream().anyMatch(e -> Objects.equals(e.getEmployeeId(), addUpDeductionRecordParam.getEmployeeId()));
        if (!employeeSameId) {
            throw new SalaryRunTimeException("员工信息不存在");
        }
        addUpDeduction.setEmployeeId(addUpDeductionRecordParam.getEmployeeId());

        String taxAgentName = addUpDeductionRecordParam.getTaxAgentName();
        if (StringUtils.isBlank(taxAgentName)) {
            //个税扣缴义务人不能为空
            throw new SalaryRunTimeException("个税扣缴义务人不能为空");
        } else {
            Optional<TaxAgentManageRangeEmployeeDTO> optionalTemp = taxAgentList.stream().filter(m -> m.getTaxAgentName().equals(taxAgentName)).findFirst();
            if (optionalTemp.isPresent()) {
                addUpDeduction.setTaxAgentId(optionalTemp.get().getTaxAgentId());
                taxAgentEmployees = optionalTemp.get().getEmployeeList();
            } else {
                //个税扣缴义务人不存在或不在权限范围内
                throw new SalaryRunTimeException("个税扣缴义务人不存在或不在权限范围内");
            }
        }

        //fixme 分权判断
//                if (openDevolution) {
//                    Optional<TaxAgentManageRangeEmployeeDTO.TaxAgentEmployee> optionalTaxAgentEmp = taxAgentEmployees.stream().filter(f -> f.getEmployeeId().equals(addUpDeduction.getEmployeeId())).findFirst();
//                    if (!optionalTaxAgentEmp.isPresent()) {
//                        Map<String, String> errorMessageMap = Maps.newHashMap();
//                        errorMessageMap.put("message", rowIndex + "该条数据不在个税扣缴义务人人员范围内，不可导入");
//                        errorData.add(errorMessageMap);
//                        errorSum += 1;
//                    }
//                }

        // 判断是否有核算过
        if (CollectionUtils.isNotEmpty(salaryAcctEmployees)) {
            Optional<SalaryAcctEmployeePO> optionalAcctEmp = salaryAcctEmployees.stream().filter(f -> f.getEmployeeId().equals(addUpDeduction.getEmployeeId()) && f.getTaxAgentId().equals(addUpDeduction.getTaxAgentId())).findFirst();
            boolean isExist = list.stream().anyMatch(f -> f.getEmployeeId().equals(addUpDeduction.getEmployeeId()) && f.getTaxAgentId().equals(addUpDeduction.getTaxAgentId()));
            if (optionalAcctEmp.isPresent() && isExist) {
                throw new SalaryRunTimeException("该年月这条数据已经核算过,不可导入");
            }
        }

        //累计子女教育
        String addUpChildEducation = addUpDeductionRecordParam.getAddUpChildEducation();
        addUpDeduction.setAddUpChildEducation(Util.null2String(addUpChildEducation));
        //累计继续教育
        String addUpContinuingEducation = addUpDeductionRecordParam.getAddUpContinuingEducation();
        addUpDeduction.setAddUpContinuingEducation(Util.null2String(addUpContinuingEducation));
        //累计住房贷款利息
        String addUpHousingLoanInterest = addUpDeductionRecordParam.getAddUpHousingLoanInterest();
        addUpDeduction.setAddUpHousingLoanInterest(Util.null2String(addUpHousingLoanInterest));
        //累计住房租金
        String addUpHousingRent = addUpDeductionRecordParam.getAddUpHousingRent();
        addUpDeduction.setAddUpHousingRent(Util.null2String(addUpHousingRent));
        //累计赡养老人
        String addUpSupportElderly = addUpDeductionRecordParam.getAddUpSupportElderly();
        addUpDeduction.setAddUpSupportElderly(Util.null2String(addUpSupportElderly));

        addUpDeduction.setAddUpIllnessMedical(Util.null2String(addUpDeductionRecordParam.getAddUpIllnessMedical()));
        addUpDeduction.setAddUpInfantCare(Util.null2String(addUpDeductionRecordParam.getAddUpInfantCare()));
        insertData.add(addUpDeduction);
        //入库
        handleImportData(insertData);
    }

    private void addLogger4Insert(Integer currentEmployeeId, AddUpDeduction addUpDeduction, String targetName) {
        String name = SalaryI18nUtil.getI18nLabel(0, "新增") + SalaryI18nUtil.getI18nLabel(0, "累计专项附加扣除");
        LoggerContext<AddUpDeduction> loggerContext = new LoggerContext<>();
        loggerContext.setTargetId(addUpDeduction.getId().toString());
        loggerContext.setTargetName(targetName);
        loggerContext.setOperateType(OperateTypeEnum.ADD.getValue());
        loggerContext.setOperateTypeName(name);
        loggerContext.setOperatedesc(name);
        loggerContext.setNewValues(addUpDeduction);
        loggerContext.setOperator(currentEmployeeId.toString());
        SalaryElogConfig.addUpDeductionLoggerTemplate.write(loggerContext);
    }

    @Override
    public void deleteSelectAddUpDeduction(AddUpDeductionRecordDeleteParam deleteParam) {
        long currentEmployeeId = user.getUID();
        // 获取所有个税扣缴义务人
        Collection<TaxAgentManageRangeEmployeeDTO> taxAgentList = getTaxAgentService(user).listTaxAgentAndEmployeeTree(currentEmployeeId);
        AddUpDeductionBiz addUpDeductionBiz = new AddUpDeductionBiz();
        String declareMonthStr = deleteParam.getDeclareMonth();
        List<Long> deleteIds = deleteParam.getIds();
        // 已经核算过的不可操作
        // 获取已经核算的数据
        List<SalaryAcctEmployeePO> salaryAcctEmployees = getAccountedEmployeeDataByTaxYearMonth(declareMonthStr);
        // 判断是否有核算过
        List<Long> deleteList = new ArrayList<>();
        ArrayList<AddUpDeduction> oldAddUpDeductions = new ArrayList<>();
        for (int i = 0; i < deleteIds.size(); i++) {
            Long id = deleteIds.get(i);
            AddUpDeduction byId = addUpDeductionBiz.getById(id);

            if (byId == null) {
                throw new SalaryRunTimeException("数据不存在或已被删除！");
            }
            // 判断是否在个税扣缴义务人范围内
            Optional<TaxAgentManageRangeEmployeeDTO> first = taxAgentList.stream().filter(m -> Objects.equals(m.getTaxAgentId(), byId.getTaxAgentId())).findFirst();
            if (!first.isPresent()) {
                throw new SalaryRunTimeException("个税扣缴义务人不存在或不在权限范围内");
            }
            // 判断用户是否存在
            if (CollectionUtils.isNotEmpty(salaryAcctEmployees)) {
                Optional<SalaryAcctEmployeePO> optionalAcctEmp = salaryAcctEmployees.stream().filter(f -> f.getEmployeeId().equals(byId.getEmployeeId()) && f.getTaxAgentId().equals(byId.getTaxAgentId())).findFirst();
                if (optionalAcctEmp.isPresent()) {
                    throw new SalaryRunTimeException("所选数据在该年月中已完成核算并归档,不可进行删除！");
                }
            }
            deleteList.add(byId.getId());
            oldAddUpDeductions.add(byId);
        }
        addUpDeductionBiz.batchDeleteByIDS(deleteList);

        //记录日志
        if (CollectionUtils.isNotEmpty(oldAddUpDeductions)) {
            oldAddUpDeductions.stream().forEach(e -> {
                LoggerContext<AddUpDeduction> loggerContext = new LoggerContext<>();
                loggerContext.setTargetId(e.getId().toString());
                loggerContext.setTargetName(SalaryI18nUtil.getI18nLabel(0, "累计专项附加扣除") + "-" + e.getId());
                loggerContext.setOperateType(OperateTypeEnum.DELETE.getValue());
                loggerContext.setOperateTypeName(SalaryI18nUtil.getI18nLabel(0, "删除"));
                loggerContext.setOperatedesc(SalaryI18nUtil.getI18nLabel(0, "累计专项附加扣除") + "-" + SalaryI18nUtil
                        .getI18nLabel(0, "删除"));
                loggerContext.setOldValues(e);
                loggerContext.setUser(user);
                SalaryElogConfig.addUpDeductionLoggerTemplate.write(loggerContext);
            });
        }
    }

    @Override
    public void deleteAllAddUpDeduction(AddUpDeductionRecordDeleteParam deleteParam) {
        String declareMonthStr = deleteParam.getDeclareMonth();
        long currentEmployeeId = user.getUID();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        // 获取所有个税扣缴义务人
        Collection<TaxAgentManageRangeEmployeeDTO> taxAgentList = getTaxAgentService(user).listTaxAgentAndEmployeeTree(currentEmployeeId);
        List<Long> taxAgentIds = taxAgentList.stream().map(TaxAgentManageRangeEmployeeDTO::getTaxAgentId).collect(Collectors.toList());
        AddUpDeductionBiz addUpDeductionBiz = new AddUpDeductionBiz();
        ArrayList<Date> declareMonthDate = new ArrayList<>();
        try {
            declareMonthDate.add(sdf.parse(declareMonthStr + "-01"));
        } catch (Exception e) {
            throw new SalaryRunTimeException("日期异常");
        }
        AddUpDeductionQueryParam queryParam = null;
        if (deleteParam.getTaxAgentId() != null && (!deleteParam.getTaxAgentId().equals(""))) {
            // 设置了个税扣缴义务人
            Long taxAgentId = SalaryEntityUtil.string2Long(deleteParam.getTaxAgentId());
            boolean canDelete = taxAgentIds.stream().anyMatch(t -> Objects.equals(t, taxAgentId));
            if (!canDelete) {
                throw new SalaryRunTimeException("个税扣缴义务人不存在或不在权限范围内!");
            }
            ArrayList<Long> tai = new ArrayList<>();
            tai.add(taxAgentId);
            queryParam = AddUpDeductionQueryParam.builder().declareMonthDate(declareMonthDate).taxAgentIds(tai).build();
        } else {
            queryParam = AddUpDeductionQueryParam.builder().declareMonthDate(declareMonthDate).taxAgentIds(taxAgentIds).build();
        }
        // 获取所有想要删除的数据
        List<AddUpDeductionDTO> list = addUpDeductionBiz.list(queryParam);
        // 获取已经核算的数据
        List<SalaryAcctEmployeePO> salaryAcctEmployees = getAccountedEmployeeDataByTaxYearMonth(declareMonthStr);
        for (AddUpDeductionDTO item : list) {
            if (CollectionUtils.isNotEmpty(salaryAcctEmployees)) {
                Optional<SalaryAcctEmployeePO> optionalAcctEmp = salaryAcctEmployees.stream().filter(f -> f.getEmployeeId().equals(item.getEmployeeId()) && f.getTaxAgentId().equals(item.getTaxAgentId())).findFirst();
                if (optionalAcctEmp.isPresent()) {
                    throw new SalaryRunTimeException("员工：" + item.getUsername() + ",在该年月中已完成核算并归档，不能进行一键清空！");
                }
            }
        }
        List<Long> deleteIds = list.stream().map(AddUpDeductionDTO::getId).collect(Collectors.toList());
        addUpDeductionBiz.batchDeleteByIDS(deleteIds);

        // 记录日志
        Collection<Long> finalTaxAgentIds = queryParam.getTaxAgentIds();
        List<String> taxAgentNames = taxAgentList.stream().filter(t -> finalTaxAgentIds.contains(t.getTaxAgentId()))
                .map(TaxAgentManageRangeEmployeeDTO::getTaxAgentName).collect(Collectors.toList());
        String name = declareMonthStr + " " + StringUtils.join(taxAgentNames, ",");
        LoggerContext<AddUpDeduction> loggerContext = new LoggerContext<>();
        loggerContext.setUser(user);
        loggerContext.setTargetName(name);
        loggerContext.setOperateType(OperateTypeEnum.CLEAR.getValue());
        loggerContext.setOperateTypeName(SalaryI18nUtil.getI18nLabel(0, "一键清空"));
        loggerContext.setOperatedesc(SalaryI18nUtil.getI18nLabel(0, "累计专项附加扣除") + "-" + SalaryI18nUtil
                .getI18nLabel(0, "一键清空：") + name);
        SalaryElogConfig.addUpDeductionLoggerTemplate.write(loggerContext);
    }

    @Override
    public AddUpDeductionRecordDTO getAddUpDeduction(AddUpDeductionQueryParam param) {
        long uid = user.getUID();
        // 获取所有个税扣缴义务人
        Collection<TaxAgentManageRangeEmployeeDTO> taxAgentList = getTaxAgentService(user).listTaxAgentAndEmployeeTree(uid);
        List<String> taxAgentNames = taxAgentList.stream().map(TaxAgentManageRangeEmployeeDTO::getTaxAgentName).collect(Collectors.toList());
        ArrayList<Long> ids = new ArrayList<>();
        ids.add(param.getId());
        AddUpDeductionQueryParam build = AddUpDeductionQueryParam.builder().ids(ids).build();
        List<AddUpDeductionRecordDTO> addUpDeductionRecordDTOS = new AddUpDeductionBiz().recordList(build);
        if (addUpDeductionRecordDTOS == null || addUpDeductionRecordDTOS.size() == 0) {
            throw new SalaryRunTimeException("该数据不存在！");
        }
        String taxAgentName = addUpDeductionRecordDTOS.get(0).getTaxAgentName();
        if (!taxAgentNames.contains(taxAgentName)) {
            throw new SalaryRunTimeException("您无权查看该数据!");
        }
        return addUpDeductionRecordDTOS.get(0);
    }

    @Override
    public String autoAddAll(Date yearMonth, Boolean isAdmin, List<Long> taxAgentIds) {
        String cacheKey = "addUpDeduction_autoAddAll_processing";
        Object objVal = Util_DataCache.getObjVal(cacheKey);
        if (objVal != null) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(135788, "一键累计过于频繁，请稍后再试 "));
        }
        try {
            Util_DataCache.setObjVal(cacheKey, true);
            //如果是定时任务直接查询所有，isAdmin传true
            boolean isChief = Boolean.TRUE.equals(isAdmin)
                    || getTaxAgentService(user).isChief((long) user.getUID());
            Collection<TaxAgentPO> taxAgents;
            if (isChief) {
                taxAgents = getTaxAgentService(user).listAll();
            } else {
                taxAgents = getTaxAgentService(user).listAllTaxAgentsAsAdmin((long) user.getUID());
            }
            // 过滤个税扣缴义务人选择框
            if (CollectionUtils.isNotEmpty(taxAgentIds)) {
                taxAgents = taxAgents.stream().filter(taxAgent -> taxAgentIds.contains(taxAgent.getId())).collect(Collectors.toList());
            }
            LocalDateTime yearMonthTime = DateUtil.toLocalDateTime(yearMonth);
            //设置时间到下一年1月1号
            Instant instant = yearMonthTime.plusYears(1L)
                    .withMonth(1).withDayOfMonth(1)
                    .atZone(ZoneOffset.systemDefault()).toInstant();
            Date nextYearStart = Date.from(instant);
            int countByDeclareAfter = getAddUpDeductionMapper()
                    .countByDeclareAfter(yearMonth, nextYearStart,
                            taxAgents.stream().map(TaxAgentPO::getId).collect(Collectors.toList())
                    );
            if (countByDeclareAfter > 0) {
                throw new SalaryRunTimeException("无法累计，请检查当前累计年度内该月后是否有累计专项附加扣除记录！");
            }
            List<AddUpDeduction> updateList = new ArrayList<>();
            List<AddUpDeduction> insertList = new ArrayList<>();
            List<Long> errorMessages = new ArrayList<>();
            List<SalaryAcctEmployeePO> accountedEmployeeData =
                    getAccountedEmployeeData(DateUtil.format(yearMonth, "yyyy-MM"));
            for (TaxAgentPO taxAgent : taxAgents) {
                List<SpecialAddDeductionPO> employeePOs = getSpecialAddDeductionService(user)
                        .getSpecialAddDeductionPOByEmployee(null, taxAgent.getId());


                //获取上月员工数据，用于累加
                LocalDateTime lastMonthDateTime = yearMonthTime.minusMonths(1);
                Map<Long, List<AddUpDeduction>> lastEmpInfo;
                if (lastMonthDateTime.getYear() == yearMonthTime.getYear()) {
                    YearMonth lastMonth = YearMonth.of(lastMonthDateTime.getYear(), lastMonthDateTime.getMonth());
                    lastEmpInfo = getEmpInfoByYearMonth(taxAgent, employeePOs, lastMonth);
                } else {
                    lastEmpInfo = new HashMap<>(0);
                }

                //获取当月员工数据，用于更新
                YearMonth currentMonth = YearMonth.of(yearMonthTime.getYear(), yearMonthTime.getMonth());
                Map<Long, List<AddUpDeduction>> currentEmpInfo = getEmpInfoByYearMonth(taxAgent, employeePOs, currentMonth);

                employeePOs.forEach(employeePO -> {
                    Long employeeId = employeePO.getEmployeeId();
                    // 如果该员工当前月份已经核算，不做累计
                    SalaryAcctEmployeePO anyAccountedEmployee = accountedEmployeeData.stream()
                            .filter(e -> e.getEmployeeId().equals(employeeId))
                            .filter(e -> e.getTaxAgentId().equals(taxAgent.getId()))
                            .findAny().orElse(null);
                    if (anyAccountedEmployee != null) {
                        errorMessages.add(employeeId);
                        return;
                    }
                    AddUpDeduction addUpDeduction = Optional.ofNullable(lastEmpInfo.get(employeeId))
                            .flatMap(list -> list.stream().findFirst())
                            .orElseGet(AddUpDeduction::new);
                    this.combine(addUpDeduction, employeePO);

                    addUpDeduction.setEmployeeId(employeeId);
                    addUpDeduction.setTaxAgentId(taxAgent.getId());
                    addUpDeduction.setDeclareMonth(DateUtil.beginOfMonth(yearMonth));
                    addUpDeduction.setCreator(user == null ? 0 : (long) user.getUID());
                    addUpDeduction.setTenantKey(DEFAULT_TENANT_KEY);

                    //确认当期是否有已经累计的记录
                    AddUpDeduction oldInfo = Optional.ofNullable(currentEmpInfo.get(employeeId))
                            .flatMap(c -> c.stream().findFirst())
                            .orElse(null);
                    Date now = new Date();
                    if (oldInfo == null) {
                        addUpDeduction.setCreateTime(now);
                        addUpDeduction.setUpdateTime(now);
                        insertList.add(encryptUtil.encrypt(addUpDeduction, AddUpDeduction.class));
                    } else {
                        addUpDeduction.setId(oldInfo.getId());
                        addUpDeduction.setCreateTime(oldInfo.getCreateTime());
                        addUpDeduction.setUpdateTime(now);
                        updateList.add(encryptUtil.encrypt(addUpDeduction, AddUpDeduction.class));
                    }
                });
            }
            Lists.partition(insertList, 100)
                    .forEach(l -> getAddUpDeductionMapper().insertData((List<AddUpDeduction>) l));
            Lists.partition(updateList, 100)
                    .forEach(l -> getAddUpDeductionMapper().updateDataAndDeclareMonth((List<AddUpDeduction>) l));

            // 记录日志
            // 根据月份、人员id查出保存的数据
            List<Long> empIds = insertList.stream().map(AddUpDeduction::getEmployeeId).collect(Collectors.toList());
            List<AddUpDeduction> insertLogList = getAddUpDeductionMapper().listSome(AddUpDeduction.builder().declareMonth(yearMonth).employeeIds(empIds).build());
            Map<String, AddUpDeduction> insertMap = SalaryEntityUtil.convert2Map(insertLogList, p -> p.getTaxAgentId() + "-" + SalaryDateUtil.getFormatYearMonth(p.getDeclareMonth()) + "-" + p.getEmployeeId());
            insertList.forEach(save -> {
                AddUpDeduction addUpDeduction = insertMap.get(save.getTaxAgentId() + "-" + SalaryDateUtil.getFormatYearMonth(save.getDeclareMonth()) + "-" + save.getEmployeeId());
                if (addUpDeduction != null) {
                    updateList.add(addUpDeduction);
                }
            });

            if (CollectionUtils.isNotEmpty(updateList)) {
                String yearMonthStr = SalaryDateUtil.getFormatYearMonth(yearMonth);
                LoggerContext loggerContext = new LoggerContext();
                loggerContext.setUser(user);
                loggerContext.setTargetName(SalaryI18nUtil.getI18nLabel(0, "一键累计 " + yearMonthStr));
                loggerContext.setOperateType(OperateTypeEnum.ADD.getValue());
                loggerContext.setOperateTypeName(SalaryI18nUtil.getI18nLabel(0, "一键累计专项附加扣除"));
                loggerContext.setOperatedesc(SalaryI18nUtil.getI18nLabel(0, "一键累计 " + yearMonthStr + " 专项附加扣除"));
                loggerContext.setNewValueList(updateList);
                SalaryElogConfig.addUpDeductionLoggerTemplate.write(loggerContext);
            }

            if (!errorMessages.isEmpty()) {
                String userNames = getSalaryEmployeeService(user)
                        .getEmployeeByIdsAll(errorMessages)
                        .stream()
                        .map(DataCollectionEmployee::getUsername)
                        .collect(Collectors.joining(","));
                return "一键累计完成！员工" + userNames + "在该年月已核算归档，跳过本次累计";
            }
            return "一键累计完成！";
        } finally {
            Util_DataCache.clearVal(cacheKey);
        }
    }

    /**
     * 对每个扣除项做加法
     *
     * @param addUpDeduction 上次记录
     * @param employeePO     专项附加扣除值
     */
    private void combine(AddUpDeduction addUpDeduction, SpecialAddDeductionPO employeePO) {
        final String zero = "0";
        String childEducation = Optional.ofNullable(addUpDeduction.getAddUpChildEducation()).orElse(zero);
        addUpDeduction.setAddUpChildEducation(plus(childEducation, employeePO.getChildrenEducation()));

        String continuingEducation = Optional.ofNullable(addUpDeduction.getAddUpContinuingEducation()).orElse(zero);
        addUpDeduction.setAddUpContinuingEducation(plus(continuingEducation, employeePO.getContinuingEducation()));

        String housingLoanInterest = Optional.ofNullable(addUpDeduction.getAddUpHousingLoanInterest()).orElse(zero);
        addUpDeduction.setAddUpHousingLoanInterest(plus(housingLoanInterest, employeePO.getHousingLoanInterest()));

        String housingRent = Optional.ofNullable(addUpDeduction.getAddUpHousingRent()).orElse(zero);
        addUpDeduction.setAddUpHousingRent(plus(housingRent, employeePO.getHousingRent()));

        String supportElderly = Optional.ofNullable(addUpDeduction.getAddUpSupportElderly()).orElse(zero);
        addUpDeduction.setAddUpSupportElderly(plus(supportElderly, employeePO.getSupportingElder()));

        String illnessMedical = Optional.ofNullable(addUpDeduction.getAddUpIllnessMedical()).orElse(zero);
        addUpDeduction.setAddUpIllnessMedical(plus(illnessMedical, employeePO.getSeriousIllnessTreatment()));

        String infantCare = Optional.ofNullable(addUpDeduction.getAddUpInfantCare()).orElse(zero);
        addUpDeduction.setAddUpInfantCare(plus(infantCare, employeePO.getInfantCare()));
    }

    private String plus(String var0, String var1) {
        return SalaryEntityUtil.string2BigDecimalDefault0(var0)
                .add(SalaryEntityUtil.string2BigDecimalDefault0(var1))
                .toString();
    }

    private Map<Long, List<AddUpDeduction>> getEmpInfoByYearMonth(TaxAgentPO taxAgent, List<SpecialAddDeductionPO> employeePOs, YearMonth lastMonth) {
        List<AddUpDeduction> addUpDeductionList = getAddUpDeductionList(lastMonth,
                employeePOs.stream().map(SpecialAddDeductionPO::getEmployeeId).collect(Collectors.toList()),
                taxAgent.getId());
        return addUpDeductionList.stream()
                .filter(addUpDeduction -> taxAgent.getId().equals(addUpDeduction.getTaxAgentId()))
                .collect(Collectors.groupingBy(AddUpDeduction::getEmployeeId));
    }


    private void checkImportParam(AddUpDeductionImportParam importParam) {


        //excel文件id
        String imageId = Util.null2String(importParam.getImageId());
        //税款所属期
        String declareMonthStr = Util.null2String(importParam.getDeclareMonth());
        //个税扣缴义务人
        String taxAgentId = Util.null2String(importParam.getTaxAgentId());

        if (StringUtils.isBlank(imageId)) {
            throw new SalaryRunTimeException("文件不存在");
        }
        if (StringUtils.isBlank(declareMonthStr)) {
            throw new SalaryRunTimeException("税款所属期为空");
        }
    }


    @Override
    public Map<String, Object> preview(AddUpDeductionImportParam importParam) {
        Map<String, Object> apidatas = new HashMap<String, Object>();
        //excel文件id
        String imageId = Util.null2String(importParam.getImageId());
        Validate.notBlank(imageId, "imageId为空");

        InputStream fileInputStream = null;

        try {
            fileInputStream = ImageFileManager.getInputStreamById(Integer.parseInt(imageId));
            Sheet sheet = ExcelSupport.parseFile(fileInputStream, 0, EXCEL_TYPE_XLSX);
            apidatas.put("headers", ExcelSupport.getSheetHeader(sheet, 0));
            apidatas.put("list", ExcelParseHelper.parse2List(sheet, 1, 0));
            return apidatas;

        } finally {
            IOUtils.closeQuietly(fileInputStream);
        }
    }


    @Override
    public AddUpDeduction getById(Long id) {
        return getAddUpDeductionMapper().getById(id);
    }

    @Override
    public PageInfo<AddUpDeductionDTO> listPage(AddUpDeductionQueryParam queryParam) {
        //申报月份
        List<String> declareMonth = queryParam.getDeclareMonth();
        if (CollectionUtils.isNotEmpty(declareMonth)) {
            queryParam.setDeclareMonth(declareMonth.stream().map(e -> e + "-01 00:00:00").collect(Collectors.toList()));
            queryParam.setDeclareMonthDate(declareMonth.stream().map(e -> e + "-01 00:00:00").map(SalaryDateUtil::dateStrToLocalTime).collect(Collectors.toList()));
        }

        long employeeId = user.getUID();

        Boolean needAuth = getTaxAgentService(user).isNeedAuth(employeeId);
        if (needAuth) {
            List<Long> taxAgentIdsAsAdmin = getTaxAgentService(user).listAllTaxAgentsAsAdmin(employeeId).stream().map(TaxAgentPO::getId).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(taxAgentIdsAsAdmin)) {
                return new PageInfo<>(AddUpDeductionDTO.class);
            }
            // 过滤义务人筛选框
            if (CollectionUtils.isNotEmpty(queryParam.getTaxAgentIds())) {
                taxAgentIdsAsAdmin = taxAgentIdsAsAdmin.stream().filter(id -> queryParam.getTaxAgentIds().contains(id)).collect(Collectors.toList());
            }
            queryParam.setTaxAgentIds(taxAgentIdsAsAdmin);
        }

        //排序配置
        OrderRuleVO orderRule = getSalarySysConfService(user).orderRule();
        queryParam.setOrderRule(orderRule);

        List<AddUpDeductionDTO> list = getAddUpDeductionMapper().list(queryParam);
        PageInfo<AddUpDeductionDTO> pageInfo = SalaryPageUtil.buildPage(queryParam.getCurrent(), queryParam.getPageSize(), list, AddUpDeductionDTO.class);
        encryptUtil.decryptList(pageInfo.getList(), AddUpDeductionDTO.class);
        return pageInfo;
    }

    @Override
    public PageInfo<AddUpDeductionRecordDTO> recordListPage(AddUpDeductionQueryParam queryParam) {
        long employeeId = (long) user.getUID();

        Boolean needAuth = getTaxAgentService(user).isNeedAuth(employeeId);
        if (needAuth) {
            List<Long> taxAgentIdsAsAdmin = getTaxAgentService(user).listAllTaxAgentsAsAdmin(employeeId).stream().map(TaxAgentPO::getId).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(taxAgentIdsAsAdmin)) {
                return new PageInfo<>(AddUpDeductionRecordDTO.class);
            }
            queryParam.setTaxAgentIds(taxAgentIdsAsAdmin);
        }

        List<AddUpDeductionRecordDTO> list = getAddUpDeductionMapper().recordList(queryParam);
        PageInfo<AddUpDeductionRecordDTO> page = SalaryPageUtil.buildPage(queryParam.getCurrent(), queryParam.getPageSize(),
                list, AddUpDeductionRecordDTO.class);
        encryptUtil.decryptList(page.getList(), AddUpDeductionRecordDTO.class);
        return page;
    }


    /**
     * 导出详情列表
     *
     * @return
     */
    @Override
    public XSSFWorkbook exportDetail(Long beLongEmployeeId, boolean isChief, AddUpDeductionQueryParam queryParam) {
        queryParam.setEmployeeId(beLongEmployeeId);
        AddUpDeductionBiz biz = new AddUpDeductionBiz();

        Long id = queryParam.getAccumulatedSpecialAdditionalDeductionId();
        if (id == null) {
            throw new SalaryRunTimeException("id不能为空");
        }

        AddUpDeduction po = biz.getById(id);
        if (po == null) {
            throw new SalaryRunTimeException(String.format("累计专项附加扣除不存在" + "[id:%s]", id));
        }

        List<DataCollectionEmployee> employeeList = getSalaryEmployeeService(user).getEmployeeByIds(Collections.singletonList(po.getEmployeeId()));
        if (CollectionUtils.isEmpty(employeeList)) {
            throw new SalaryRunTimeException("员工信息不存在");
        }

        //查询参数
        queryParam.setEmployeeId(po.getEmployeeId());
        //申报月份
        List<String> declareMonth = queryParam.getDeclareMonth();
        if (CollectionUtils.isNotEmpty(declareMonth)) {
            queryParam.setDeclareMonth(declareMonth.stream().map(e -> e + "-01 00:00:00").collect(Collectors.toList()));
            queryParam.setDeclareMonthDate(declareMonth.stream().map(e -> e + "-01 00:00:00").map(SalaryDateUtil::dateStrToLocalTime).collect(Collectors.toList()));
        }


        //获取操作按钮资源
        List<List<String>> rowList = getExcelRowDetailList(isChief, queryParam);


        //获取excel
        return ExcelUtil.genWorkbook(rowList, "累计专项附加扣除明细");
    }


    /**
     * 导出详情
     *
     * @param param
     * @return
     */
    private List<List<String>> getExcelRowDetailList(boolean isChief, AddUpDeductionQueryParam param) {
        long employeeId = user.getUID();

        //excel标题
        List<String> title = Arrays.asList("姓名", "申报月份", "个税扣缴义务人", "部门", "工号", "累计子女教育", "累计继续教育", "累计住房贷款利息", "累计住房租金", "累计赡养老人", "累计大病医疗", "累计婴幼儿照护");


        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM");
        //查询详细信息
        List<AddUpDeductionRecordDTO> list = new AddUpDeductionBiz().recordList(param);
        final List<List<String>> dataRowList = Optional.ofNullable(list)
                .map(List::stream)
                .map(operatorStream -> operatorStream.map(dto -> {
                    List<String> cellList = new ArrayList<>();
                    cellList.add(Util.null2String(dto.getUsername()));
                    cellList.add(Util.null2String(dto.getDeclareMonth() == null ? "" : formatter.format(dto.getDeclareMonth())));
                    cellList.add(Util.null2String(dto.getTaxAgentName()));
                    cellList.add(Util.null2String(dto.getDepartmentName()));
                    cellList.add(Util.null2String(dto.getJobNum()));
                    cellList.add(Util.null2String(dto.getAddUpChildEducation()));
                    cellList.add(Util.null2String(dto.getAddUpContinuingEducation()));
                    cellList.add(Util.null2String(dto.getAddUpHousingLoanInterest()));
                    cellList.add(Util.null2String(dto.getAddUpHousingRent()));
                    cellList.add(Util.null2String(dto.getAddUpSupportElderly()));
                    cellList.add(Util.null2String(dto.getAddUpIllnessMedical()));
                    cellList.add(Util.null2String(dto.getAddUpInfantCare()));
                    return cellList;
                }).collect(Collectors.toList()))
                .orElse(Collections.emptyList());


        // 开启分权并且不是薪酬模块总管理员
        if (getTaxAgentService(user).isOpenDevolution() && !isChief) {
            List<TaxAgentEmployeeDTO> taxAgentEmployees = getTaxAgentService(user).listTaxAgentAndEmployee(employeeId);
            List<Long> taxAgentIdsAsAdmin = getTaxAgentService(user).listAllTaxAgentsAsAdmin(employeeId).stream().map(TaxAgentPO::getId).collect(Collectors.toList());
//            List<AddUpDeduction> lastList = getLastListByModifier(employeeId, tenantKey);
            list = list.stream().filter(f ->
                    // 作为管理员
                    taxAgentIdsAsAdmin.contains(f.getTaxAgentId())
                            // 作为分管理员
                            || TaxAgentBO.checkTaxAgentAndEmployee(taxAgentEmployees, f.getTaxAgentId(), f.getEmployeeId())
            ).collect(Collectors.toList());
        }


        List<List<String>> rowList = new ArrayList<>();
        rowList.add(title);
        rowList.addAll(dataRowList);
        return rowList;
    }


    @Override
    public List<AddUpDeduction> getAddUpDeductionList(YearMonth declareMonth, List<Long> employeeIds, Long taxAgentId) {
        AddUpDeductionBiz addUpDeductionBiz = new AddUpDeductionBiz();
        if (declareMonth == null) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(100342, "参数有误：申报月份、租户key必传"));
        }
        return addUpDeductionBiz.listSome(AddUpDeduction.builder().declareMonth(SalaryDateUtil.toDateStartOfMonth(declareMonth)).employeeIds(employeeIds).taxAgentId(taxAgentId).build());
    }

    @Override
    public XSSFWorkbook downloadTemplate(boolean isChief, AddUpDeductionQueryParam queryParam) {

        String sheetName = SalaryI18nUtil.getI18nLabel(101603, "累计专项附加扣除导入模板");
        String[] header = {
                SalaryI18nUtil.getI18nLabel(85429, "姓名"),
                SalaryI18nUtil.getI18nLabel(86184, "个税扣缴义务人"),
                SalaryI18nUtil.getI18nLabel(86185, "部门"),
                SalaryI18nUtil.getI18nLabel(86186, "手机号"),
                SalaryI18nUtil.getI18nLabel(86317, "工号"),
                SalaryI18nUtil.getI18nLabel(86318, "证件号码"),
                SalaryI18nUtil.getI18nLabel(86319, "入职日期"),
                SalaryI18nUtil.getI18nLabel(86321, "累计子女教育"),
                SalaryI18nUtil.getI18nLabel(86323, "累计继续教育"),
                SalaryI18nUtil.getI18nLabel(86324, "累计住房贷款利息"),
                SalaryI18nUtil.getI18nLabel(86325, "累计住房租金"),
                SalaryI18nUtil.getI18nLabel(86326, "累计赡养老人"),
                SalaryI18nUtil.getI18nLabel(105142, "累计大病医疗"),
                SalaryI18nUtil.getI18nLabel(105142, "累计婴幼儿照护")
        };

        List<Object> headerList = Arrays.asList(header);

        //查询当前申报月份和个税扣缴义务人的累计专项附加扣除表数据
        List<List<Object>> rows = new ArrayList<>();
        rows.add(headerList);
        if (queryParam.isHasData()) {
            List<List<Object>> datas = getLists(queryParam);
            rows.addAll(datas);
        }

        // 4.注释
        List<ExcelComment> excelComments = Lists.newArrayList();
        excelComments.add(new ExcelComment(0, 0, 3, 2, SalaryI18nUtil.getI18nLabel(100344, "必填")));
        excelComments.add(new ExcelComment(1, 0, 4, 2, SalaryI18nUtil.getI18nLabel(100344, "必填")));
        excelComments.add(new ExcelComment(7, 0, 10, 2, SalaryI18nUtil.getI18nLabel(100344, "输入数字")));
        excelComments.add(new ExcelComment(8, 0, 11, 2, SalaryI18nUtil.getI18nLabel(100344, "输入数字")));
        excelComments.add(new ExcelComment(9, 0, 12, 2, SalaryI18nUtil.getI18nLabel(100344, "输入数字")));
        excelComments.add(new ExcelComment(10, 0, 13, 2, SalaryI18nUtil.getI18nLabel(100344, "输入数字")));
        excelComments.add(new ExcelComment(11, 0, 14, 2, SalaryI18nUtil.getI18nLabel(100344, "输入数字")));
        excelComments.add(new ExcelComment(12, 0, 15, 2, SalaryI18nUtil.getI18nLabel(100344, "输入数字")));
        excelComments.add(new ExcelComment(13, 0, 16, 2, SalaryI18nUtil.getI18nLabel(100344, "输入数字")));

        XSSFWorkbook book = ExcelUtil.genWorkbookV2(rows, sheetName, excelComments);

        return book;
    }


    @Override
    public XSSFWorkbook export(AddUpDeductionQueryParam queryParam) {
        //excel标题
        List<Object> title = Arrays.asList("姓名", "个税扣缴义务人", "部门", "手机号", "工号", "证件号码", "入职日期", "累计子女教育", "累计继续教育", "累计住房贷款利息", "累计住房租金", "累计赡养老人", "累计大病医疗", "累计婴幼儿照护");

        List<List<Object>> dataRowList = getLists(queryParam);

        List<List<Object>> rowList = new ArrayList<>();
        rowList.add(title);

        rowList.addAll(dataRowList);

        // 记录日志
        String name = SalaryI18nUtil.getI18nLabel(0, "导出");
        LoggerContext<AddUpDeduction> loggerContext = new LoggerContext<>();
        loggerContext.setTargetName(SalaryI18nUtil.getI18nLabel(0, "累计专项附加扣除"));
        loggerContext.setOperateType(OperateTypeEnum.EXCEL_EXPORT.getValue());
        loggerContext.setOperateTypeName(name);
        loggerContext.setOperatedesc(SalaryI18nUtil.getI18nLabel(0, "累计专项附加扣除") + "-" + name);
        loggerContext.setUser(user);
        SalaryElogConfig.addUpDeductionLoggerTemplate.write(loggerContext);

        //获取excel
        return ExcelUtil.genWorkbookV2(rowList, "累计专项附加扣除");
    }

    private List<List<Object>> getLists(AddUpDeductionQueryParam param) {

        long uid = user.getUID();
        //排序配置
        OrderRuleVO orderRule = getSalarySysConfService(user).orderRule();
        param.setOrderRule(orderRule);

        List<AddUpDeductionDTO> list = new AddUpDeductionBiz().list(param);

        // 开启分权并且不是薪酬模块总管理员
        if (getTaxAgentService(user).isNeedAuth(uid)) {
            List<TaxAgentEmployeeDTO> taxAgentEmployees = getTaxAgentService(user).listTaxAgentAndEmployee(uid);
            List<Long> taxAgentIdsAsAdmin = getTaxAgentService(user).listAllTaxAgentsAsAdmin(uid).stream().map(TaxAgentPO::getId).collect(Collectors.toList());
            list = list.stream().filter(f ->
                    // 作为管理员
                    taxAgentIdsAsAdmin.contains(f.getTaxAgentId())
                            // 作为分管理员
                            || TaxAgentBO.checkTaxAgentAndEmployee(taxAgentEmployees, f.getTaxAgentId(), f.getEmployeeId())
            ).collect(Collectors.toList());
        }

        List<List<Object>> dataRowList = Optional.ofNullable(list)
                .map(List::stream)
                .map(operatorStream -> operatorStream.map(dto -> {
                    List<Object> cellList = new ArrayList<>();
                    cellList.add(Util.null2String(dto.getUsername()));
                    cellList.add(Util.null2String(dto.getTaxAgentName()));
                    cellList.add(Util.null2String(dto.getDepartmentName()));
                    cellList.add(Util.null2String(dto.getMobile()));
                    cellList.add(Util.null2String(dto.getJobNum()));
                    cellList.add(Util.null2String(dto.getIdNo()));
                    cellList.add(Util.null2String(dto.getHiredate()));
                    cellList.add(Util.null2String(dto.getAddUpChildEducation()));
                    cellList.add(Util.null2String(dto.getAddUpContinuingEducation()));
                    cellList.add(Util.null2String(dto.getAddUpHousingLoanInterest()));
                    cellList.add(Util.null2String(dto.getAddUpHousingRent()));
                    cellList.add(Util.null2String(dto.getAddUpSupportElderly()));
                    cellList.add(Util.null2String(dto.getAddUpIllnessMedical()));
                    cellList.add(Util.null2String(dto.getAddUpInfantCare()));
                    return cellList;
                }).collect(Collectors.toList()))
                .orElse(Collections.emptyList());
        return dataRowList;
    }


    /**
     * 根据年月获取已核算数据
     *
     * @param yearMonth
     * @return
     */
    @Override
    public List<SalaryAcctEmployeePO> getAccountedEmployeeData(String yearMonth) {
        List<SalaryAcctEmployeePO> list = Lists.newArrayList();
        LocalDate salaryMonthDate = LocalDate.parse(yearMonth + "-01", SalaryDateUtil.DATE_FORMATTER);
        List<SalaryAcctRecordPO> salaryAcctRecords = getSalaryAcctRecordService(user).listBySalaryMonth(LocalDateRange.builder().fromDate(SalaryDateUtil.localDateToDate(salaryMonthDate)).endDate(SalaryDateUtil.localDateToDate(salaryMonthDate)).build());
        salaryAcctRecords.forEach(e -> {
            boolean isAccounted = e.getStatus() > SalaryAcctRecordStatusEnum.NOT_ARCHIVED.getValue();
            if (isAccounted) {
//                list.addAll(getSalaryAcctEmployeeService(user).listBySalaryAcctRecordIds(Collections.singleton(salaryAcctRecords.get(0).getId())));
                list.addAll(getSalaryAcctEmployeeService(user).listBySalaryAcctRecordIds(Collections.singleton(e.getId())));
            }
        });

        return list;
    }

    @Override
    public List<SalaryAcctEmployeePO> getAccountedEmployeeDataByTaxYearMonth(String yearMonth) {
        List<SalaryAcctEmployeePO> list = Lists.newArrayList();
        YearMonth month = YearMonth.parse(yearMonth);
        LocalDate salaryMonthDate = month.atDay(1);
        LocalDate salaryMonthEndDate = month.atEndOfMonth();
        List<SalaryAcctRecordPO> salaryAcctRecords = getSalaryAcctRecordService(user).listByTaxCycle(
                LocalDateRange.builder().fromDate(SalaryDateUtil.localDateToDate(salaryMonthDate))
                        .endDate(SalaryDateUtil.localDateToDate(salaryMonthEndDate)).build(),
                null);
        salaryAcctRecords.forEach(e -> {
            boolean isAccounted = e.getStatus() > SalaryAcctRecordStatusEnum.NOT_ARCHIVED.getValue();
            if (isAccounted) {
//                list.addAll(getSalaryAcctEmployeeService(user).listBySalaryAcctRecordIds(Collections.singleton(salaryAcctRecords.get(0).getId())));
                list.addAll(getSalaryAcctEmployeeService(user).listBySalaryAcctRecordIds(Collections.singleton(e.getId())));
            }
        });

        return list;
    }

}
