package com.yonyou.hrcloud.attentrpt.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yonyou.hrcloud.adder.context.AppContext;
import com.yonyou.hrcloud.adder.dao.IGenericDao;
import com.yonyou.hrcloud.adder.exception.AdderBusinessException;
import com.yonyou.hrcloud.adder.helper.BatchHelper;
import com.yonyou.hrcloud.adder.helper.IDHelper;
import com.yonyou.hrcloud.adder.model.param.Sorter;
import com.yonyou.hrcloud.adder.model.query.ChainLogicExpression;
import com.yonyou.hrcloud.adder.model.query.Condition;
import com.yonyou.hrcloud.adder.model.query.Operator;
import com.yonyou.hrcloud.adder.model.query.ValueExpression;
import com.yonyou.hrcloud.adder.service.IGenericService;
import com.yonyou.hrcloud.attend.helper.IAttendConst;
import com.yonyou.hrcloud.attend.model.*;
import com.yonyou.hrcloud.attend.service.*;
import com.yonyou.hrcloud.attentrpt.dao.IAttendRptCalDao;
import com.yonyou.hrcloud.attentrpt.helper.utils.TsAttendRptUtils;
import com.yonyou.hrcloud.attentrpt.model.*;
import com.yonyou.hrcloud.attentrpt.service.ITsAttendRptAsyncService;
import com.yonyou.hrcloud.attentrpt.service.ITsAttendRptCoreBusinessService;
import com.yonyou.hrcloud.attentrpt.service.ITsAttendRptService;
import com.yonyou.hrcloud.attentrpt.service.ITsAttentRptMonCalService;
import com.yonyou.hrcloud.attentrpt.util.AttendRptUtil;
import com.yonyou.hrcloud.berry.helper.DateStyle;
import com.yonyou.hrcloud.berry.helper.DateUtil;
import com.yonyou.hrcloud.businesstrip.helper.convertor.TsBusinessVariableConvertor;
import com.yonyou.hrcloud.calendar.model.AttendCalendar;
import com.yonyou.hrcloud.calendar.service.IAttendCalendarHistoryService;
import com.yonyou.hrcloud.calendar.service.IAttendCalendarService;
import com.yonyou.hrcloud.formula.helper.FormulaUtils;
import com.yonyou.hrcloud.formula.model.FormulaBaseVO;
import com.yonyou.hrcloud.holiday.model.Holiday;
import com.yonyou.hrcloud.holiday.model.WorkingCalendar;
import com.yonyou.hrcloud.holiday.service.IHolidayService;
import com.yonyou.hrcloud.leave.helper.convertor.TsLeaveVariableConvertor;
import com.yonyou.hrcloud.leave.model.LeaveStoreCalParams;
import com.yonyou.hrcloud.leave.service.ILeaveStoreService;
import com.yonyou.hrcloud.support.MqMsgSupport;
import com.yonyou.hrcloud.tcommon.helper.HolidayHelper;
import com.yonyou.hrcloud.tcommon.helper.convertor.TsFormulaAttendStaffConvertor;
import com.yonyou.hrcloud.tcommon.model.TimeConst;
import com.yonyou.hrcloud.tcommon.service.ItimeCommonService;
import com.yonyou.hrcloud.tcommon.utils.ElasticShiftUtils;
import com.yonyou.hrcloud.tcommon.utils.TimePubUtils;
import com.yonyou.hrcloud.timeitem.helper.ITsFormulaConstant;
import com.yonyou.hrcloud.timeitem.helper.TsMDRptEnum;
import com.yonyou.hrcloud.timeitem.helper.convertor.*;
import com.yonyou.hrcloud.timeitem.impl.TsAttendRptBasicCalServiceImpl;
import com.yonyou.hrcloud.timeitem.model.TimeItemVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.time.DateUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.data.domain.Sort;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 考勤月报计算服务
 * Created by hongwei.xu on 2017/9/11.
 */
@Slf4j
@Service
@Transactional
@Scope("prototype")
public class TsAttentRptMonthCalServiceImpl extends TsAttendRptBasicCalServiceImpl implements ITsAttentRptMonCalService {

    @Resource
    private IShiftService iShiftService;
    @Resource
    private IAttendPeriodService iAttendPeriodService;
    @Resource
    private IAttendStaffService iAttendStaffService;
    @Resource
    private IAttendRptCalDao attendRptCalDao;
    @Resource
    @Lazy
    private ITsAttendRptService iTsAttendRptService;
    @Resource
    @Lazy
    private ITsAttendRptCoreBusinessService iTsAttendRptCoreBusinessService;
    @Resource
    private IAttendCalendarService staffCalendarService;
    @Resource
    private ItimeCommonService itimeCommonService;
    @Resource
    private IAttendSchemeService iAttendSchemeService;
    @Resource
    private IAttendCalendarService iAttendCalendarService;
    @Resource
    private IAttendRuleService iAttendRuleService;
    @Resource
    private IGenericService genericService;
    @Resource
    private IHolidayService iHolidayService;
    @Resource
    private ILeaveStoreService storeService;
    @Resource
    private IAttendConfigService configService;
    @Autowired
    private ITsAttendRptAsyncService attendRptAsyncService;
    @Autowired
    private HolidayHelper holidayHelper;
    @Autowired
    private IAttendCalendarHistoryService attendCalendarHistoryService;

    @Override
    public List<Class> getExtConvertors() {
        List<Class> list = new ArrayList<Class>();
        //考勤月报数据翻译
        list.add(TsMonthRptVariableConvertor.class);
        //考勤上月月报数据翻译
        list.add(TsPreMonthRptVariableConvertor.class);
        //考勤日报汇总数据
        list.add(TsDayRptSumVariableConvertor.class);
        //休假数据
        list.add(TsBusinessVariableConvertor.class);
        list.add(TsLeaveVariableConvertor.class);
        //考勤数据
        list.add(TsMonthRptAttendDataVariableConvertor.class);
        //期间
        list.add(TsMonthRptAttendPeriodVariableConvertor.class);
        //考勤信息
        list.add(TsFormulaAttendStaffConvertor.class);
        //考勤月报弹性规则
        list.add(TsMonthRptElasticRuleVariableConvertor.class);
        return list;
    }

    @Override
    @Transactional
    public AttendRptCalResult calculateRpt(String year, String month, List<AttendMonthRptVO> attendMonRptVOList,
                                List<TimeItemVO> timeItemVOList, List<TimeItemVO> extTimeItemVOList, AttendSchemeVO attendSchemeVO, boolean isTask) {
        List<String> list = new ArrayList<>();
        list.add(year+month);
        log.debug("#####attend 月报计算1：calculateRpt queryListByPeriods begin");
        List<AttendPeriod>  periodList = iAttendPeriodService.queryListByPeriods(list, attendSchemeVO.getPeriodSchemeId());
        log.debug("#####attend 月报计算1：calculateRpt queryListByPeriods end");
        if (CollectionUtils.isEmpty(periodList)) {
            throw new AdderBusinessException("未设置考勤期间!","attend.report.i18n60");
        }
        return calculateRpt_inner(year, month, attendMonRptVOList,timeItemVOList, extTimeItemVOList, attendSchemeVO,periodList.get(0), isTask,null);
    }

    /***
     * 内部调用
     * @param year year
     * @param month month
     * @param attendMonRptVOList attendMonRptVOList
     * @param timeItemVOList timeItemVOList
     * @param extTimeItemVOList extTimeItemVOList
     * @param attendSchemeVO attendSchemeVO
     * @param period period
     * @return
     */
    private AttendRptCalResult calculateRpt_inner(String year, String month, List<AttendMonthRptVO> attendMonRptVOList,
                                List<TimeItemVO> timeItemVOList, List<TimeItemVO> extTimeItemVOList, AttendSchemeVO attendSchemeVO,AttendPeriod period, boolean isAuditCal,String logId) {
        TsAttendRptCalParamVO tsAttendRptCalParamVO = new TsAttendRptCalParamVO();
        tsAttendRptCalParamVO.setYear(year);
        tsAttendRptCalParamVO.setMonth(month);
        tsAttendRptCalParamVO.setTask(false);
        tsAttendRptCalParamVO.setLogId(logId);
        tsAttendRptCalParamVO.setFormulaVariableXmlPath(ITsFormulaConstant.FORMULA_VARIABLE_XML_PATH_MONTH);
        tsAttendRptCalParamVO.setAttendSchemeVO(attendSchemeVO);
        if(period != null) {
            tsAttendRptCalParamVO.setBeginDate(period.getBegindate());
            tsAttendRptCalParamVO.setEndDate(period.getEnddate());
        }
        tsAttendRptCalParamVO.setLstTimeItemVO(timeItemVOList);
        log.debug("#####attend 月报计算1：calculateRpt initPubEnvironmentVariables begin");
        //初始考勤档案数据
        initPubEnvironmentVariables(attendMonRptVOList, tsAttendRptCalParamVO);
        log.debug("#####attend 月报计算1：calculateRpt initPubEnvironmentVariables end");
        //初始化本月考勤日报汇总数据 只包括整形和数值型
        List<String> staffIds = FormulaUtils.getPropertyListFromBeanVO(attendMonRptVOList, "staffId");
        Map<String, AttendDayRptVO> sumDayDatas = querySumDayByConditionDiwork(staffIds.toArray(new String[0]), tsAttendRptCalParamVO);//获取日报汇总日报数据
        boolean singleStaff = CollectionUtils.isNotEmpty(staffIds) && staffIds.size() == 1;
        if(singleStaff){
            log.error("#####attend 月报计算({})-sumDayDatas---{}",staffIds.get(0),JSONObject.toJSONString(sumDayDatas));
        }
        int index = 0;
        boolean ishascheck = true;
        List<String> calLists = new ArrayList<>();
        List<AttendMonthRptVO> monthRptVOList = new ArrayList<>();

        if(singleStaff){
            log.error("##attend-月报计算({})-attendMonRptVOList-{}",staffIds.get(0),JSONObject.toJSONString(attendMonRptVOList));
        }
        for (AttendMonthRptVO rptVO : attendMonRptVOList) {
            AttendRptUtil.processCustomeMonthItem(rptVO);
            //审核过的记录不需要进行计算
            if (rptVO.getIsapprove() != null && rptVO.getIsapprove()) {
                MqMsgSupport.logMsg(logId,String.format("###attend-月报状态已审核，跳过,staff-%s(%s),orgid-%s,begindate-%s,enddate-%s",rptVO.getStaffName(),rptVO.getStaffId(),rptVO.getOrgId(),tsAttendRptCalParamVO.getBeginDate(),tsAttendRptCalParamVO.getEndDate()));
                continue;
            }
            tsAttendRptCalParamVO.setSumDayRtpDatas(getStaffSumData(sumDayDatas,rptVO.getStaffId(),rptVO.getOrgId()));
            //计算公式项目
            super.calculateRpt(rptVO, timeItemVOList,tsAttendRptCalParamVO);
            //计算固定列
            calculateRptFixItems(rptVO,tsAttendRptCalParamVO);
            //计算动态扩展列
            calculateExtRptItem(rptVO, extTimeItemVOList,tsAttendRptCalParamVO);

            if(rptVO.getWorkdays() == null || rptVO.getWorkdays().doubleValue() == 0) {
                MqMsgSupport.logMsg(logId,String.format("###attend-月报应出勤天数为0，跳过,staff-%s(%s),orgid-%s,begindate-%s,enddate-%s",rptVO.getStaffName(),rptVO.getStaffId(),rptVO.getOrgId(),tsAttendRptCalParamVO.getBeginDate(),tsAttendRptCalParamVO.getEndDate()));
                continue;
            }

            if (!StringUtils.isEmpty(rptVO.getString("id"))) {
                calLists.add(rptVO.getString("id"));
            }
            rptVO.setBegindate(tsAttendRptCalParamVO.getBeginDate());//开始日期
            rptVO.setEnddate(tsAttendRptCalParamVO.getEndDate());//结束日期
            rptVO.setLimittime(getLimitTime(rptVO,tsAttendRptCalParamVO));//补考勤日期限制
            
//            //chenzf 计算 begin
//            log.debug("#####attend 查询子表1：attendRptCalDao queryGlbdef3 begin");
//            List<Map> list = attendRptCalDao.queryGlbdef3(rptVO.getStaffId(), year, month);
//            log.debug("#####attend 查询子表1：attendRptCalDao queryGlbdef3 end");
//            if(list != null && list.size() > 0) {
//            	log.debug("#####attend 查询子表1：attendRptCalDao queryGlbdef3 glbdef3:"+ JSON.toJSONString(list));
//            	Map<String, Object> glbdef3Map = list.get(0);
//            	if(glbdef3Map != null && glbdef3Map.size() > 0) {
//	            	//平时中午加班次数
//	            	rptVO.setF_n_1(new BigDecimal(glbdef3Map.get("glbdef3") == null ? "0" : glbdef3Map.get("glbdef3").toString()));
//	            	//平时晚上加班次数
//	            	rptVO.setF_n_2(new BigDecimal(glbdef3Map.get("glbdef4") == null ? "0" : glbdef3Map.get("glbdef4").toString()));
//	            	//周末加班次数
//	            	rptVO.setF_n_3(new BigDecimal(glbdef3Map.get("glbdef5") == null ? "0" : glbdef3Map.get("glbdef5").toString()));
//	            	//节假日加班次数
//	            	rptVO.setF_n_4(new BigDecimal(glbdef3Map.get("glbdef6") == null ? "0" : glbdef3Map.get("glbdef6").toString()));
//	            	//平时加班时长
//	            	rptVO.setF_n_5(new BigDecimal(glbdef3Map.get("glbdef7") == null ? "0" : glbdef3Map.get("glbdef7").toString()));
//	            	//周末加班时长
//	            	rptVO.setF_n_6(new BigDecimal(glbdef3Map.get("glbdef8") == null ? "0" : glbdef3Map.get("glbdef8").toString()));
//	            	//节假日加班时长
//	            	rptVO.setF_n_7(new BigDecimal(glbdef3Map.get("glbdef9") == null ? "0" : glbdef3Map.get("glbdef9").toString()));
//            	}
//            }
//            //chenzf 计算 end
            
            monthRptVOList.add(rptVO);
            log.info("calculateRpt:" + index++);
        }

        List<String> staffids = null;
        if (CollectionUtils.isNotEmpty(monthRptVOList)) {
            staffids = FormulaUtils.getPropertyListFromBeanVO(monthRptVOList, "staffId");
        }
        log.debug("#####attend 月报计算1：calculateRpt deleteRpts begin");
        deleteRpts(calLists, staffids,tsAttendRptCalParamVO.getYear(),tsAttendRptCalParamVO.getMonth(),attendSchemeVO.getOrgs(),isAuditCal);
        log.debug("#####attend 月报计算1：calculateRpt deleteRpts end-monthRptVOList:{}",monthRptVOList.size());

        //重新插入数据
        if (CollectionUtils.isNotEmpty(monthRptVOList)) {
            MqMsgSupport.logMsg(logId,String.format("######ready for 计算后插入数据(batchInsertMonthRptVos):year-%s,month-%s,计算数量-%s，生成数量-%s",year,month,attendMonRptVOList.size(),monthRptVOList.size()));
            log.debug("#####attend 月报计算1：calculateRpt batchInsertDayRptVos begin");
            
            for (AttendMonthRptVO rptVO : monthRptVOList) {
            	//chenzf 计算 begin
	        	log.debug("#####attend 查询子表1：attendRptCalDao queryGlbdef3 begin");
	        	String month1 = month;
	        	if(month != null && month.length() == 1) {
	        		month1 = "0" + month;
	        	}
	        	List<Map> list = attendRptCalDao.queryGlbdef3(rptVO.getStaffId(), year, month1);
	        	log.debug("#####attend 查询子表1：attendRptCalDao queryGlbdef3 end");
	        	if(list != null && list.size() > 0) {
	            	log.debug("#####attend 查询子表1：attendRptCalDao queryGlbdef3 glbdef3:"+ JSON.toJSONString(list));
	              	Map<String, Object> glbdef3Map = list.get(0);
	              	if(glbdef3Map != null && glbdef3Map.size() > 0) {
	  	            	//平时中午加班次数
	  	            	rptVO.setF_n_1(new BigDecimal(glbdef3Map.get("GLBDEF3") == null ? "0" : glbdef3Map.get("GLBDEF3").toString()));
	  	            	//平时晚上加班次数
	  	            	rptVO.setF_n_2(new BigDecimal(glbdef3Map.get("GLBDEF4") == null ? "0" : glbdef3Map.get("GLBDEF4").toString()));
	  	            	//周末加班次数
	  	            	rptVO.setF_n_3(new BigDecimal(glbdef3Map.get("GLBDEF5") == null ? "0" : glbdef3Map.get("GLBDEF5").toString()));
	  	            	//节假日加班次数
	  	            	rptVO.setF_n_4(new BigDecimal(glbdef3Map.get("GLBDEF6") == null ? "0" : glbdef3Map.get("GLBDEF6").toString()));
	  	            	//平时加班时长
	  	            	rptVO.setF_n_5(new BigDecimal(glbdef3Map.get("GLBDEF7") == null ? "0" : glbdef3Map.get("GLBDEF7").toString()));
	  	            	//周末加班时长
	  	            	rptVO.setF_n_6(new BigDecimal(glbdef3Map.get("GLBDEF8") == null ? "0" : glbdef3Map.get("GLBDEF8").toString()));
	  	            	//节假日加班时长
	  	            	rptVO.setF_n_7(new BigDecimal(glbdef3Map.get("GLBDEF9") == null ? "0" : glbdef3Map.get("GLBDEF9").toString()));
	            	}
	        	}
              //chenzf 计算 end
            }
            
            batchInsertDayRptVos(monthRptVOList, timeItemVOList, getFixItems());
            log.debug("#####attend 月报计算1：calculateRpt batchInsertDayRptVos end");
        }

        AttendConfig config = configService.getCurConfig(AppContext.getContext().getScope());
        log.error("#####attend 月报存休计算,config:"+ JSONObject.toJSONString(config));
        //计算存休
        boolean itemStoreExist = timeItemVOList.stream().filter(item -> "store".equals(item.getCode())).count() > 0;
        if(itemStoreExist && CollectionUtils.isNotEmpty(staffids) && config != null && config.isUseStore()) {
            boolean isCarry = DateUtils.isSameDay(new Date(), tsAttendRptCalParamVO.getEndDate());
            MqMsgSupport.logMsg(logId,"#####attend 月报计算,isCarry:"+ (isCarry ? "1" : "0"));
            processStore(monthRptVOList,staffids, null,attendSchemeVO.getPeriodSchemeId() ,year, month);
        }
        if(CollectionUtils.isNotEmpty(staffids) && config.isSynNc()) {
            MqMsgSupport.logMsg(logId,"#####attend 月报同步开始:");
            AttendRptAuditParams auditParams = new AttendRptAuditParams();
            auditParams.setYear(year);
            auditParams.setMonth(month);
            auditParams.setEnddate(period.getEnddate());
            auditParams.setStaffIds(staffids);
            auditParams.setSchemeId(attendSchemeVO.getId());
            auditParams.setTenantId(AppContext.getContext().getScope());
            auditParams.setUserId(AppContext.getContext().getUserId());
            auditParams.setSynMode(AttendRptAuditParams.SYN_MODE_CAL);
            //attendRptAsyncService.processMonthDataSyn2NCC(auditParams, attendSchemeVO, NCCAsyncParam.generate());
        }
        return new AttendRptCalResult(logId,attendMonRptVOList.size(),monthRptVOList.size(),"月报生成成功！");
    }

    private Map<String, AttendDayRptVO> getStaffSumData(Map<String, AttendDayRptVO> mapSumDayDatas,String staffId,String orgId){
        Map<String, AttendDayRptVO> mapResult = new HashMap<>();
        String mapKey = String.format("%s_%s",staffId,orgId);
        AttendDayRptVO sumData = mapSumDayDatas.get(mapKey);
        if(sumData == null) {
            return mapResult;
        }
        mapResult.put(staffId,sumData);
        return mapResult;
    }

    @Async
    public void processStore(List<AttendMonthRptVO> monthRptVOList,List<String> staffIds,List<String> deptIds,String peiodMainid,String year,String month){
        try {
            LeaveStoreCalParams leaveStoreCalParams=new LeaveStoreCalParams(
                    AppContext.getContext().getScope(),AppContext.getContext().getUserId(),
                    peiodMainid,year,month,staffIds,deptIds
            );
            TsAttendRptUtils.doGetLeaveStoreMonthDataForMonth(leaveStoreCalParams,monthRptVOList);
            storeService.calTotalNewSync(leaveStoreCalParams);
            //storeService.(staffIds, deptIds,peiodMainid,year, month,AppContext.getContext().getScope(),AppContext.getContext().getUserId());
        }catch (Exception ex){
            log.error("#####attend    error:",ex);
        }
    }


    /***
     * 获取人员考勤规则中的补考勤限制日期
     * @param rptVO
     * @param attendRptCalParamVO
     * @return
     */
    private int getLimitTime(AttendMonthRptVO rptVO,TsAttendRptCalParamVO attendRptCalParamVO){
        Map<String,TsAttendRuleVO> rules = attendRptCalParamVO.getMapAttendRuleVO();
        if(StringUtils.isEmpty(rptVO.getStaffId()) || rules == null || rules.size() == 0) {
            return 0;
        }
        TsAttendRuleVO rule = rules.get(rptVO.getStaffId());
        return rule == null ? 0 : rule.getLimitTime();
    }

    @Override
    @Transactional
    public AttendRptCalResult calculateRpt(String year, String month, String[] staffids, List<TimeItemVO> timeItemVOList,
                                List<TimeItemVO> extTimeItemVOList, String attendSchemeId) {
        if (staffids == null || staffids.length == 0) {
            throw new AdderBusinessException("没有可计算人员!","attend.report.i18n41");
        }
        if (StringUtils.isEmpty(attendSchemeId)) {
            log.error("————————————没有对应的假勤方案!————————————————");
            throw new AdderBusinessException("没有对应的假勤方案!","attend.report.i18n59");
        }
        AttendSchemeVO attendSchemeVO = iAttendSchemeService.queryAttendSchemeById(attendSchemeId);
        if (attendSchemeVO == null) {
            log.error("————————————没有对应的假勤方案!————————————————");
            throw new AdderBusinessException("没有对应的假勤方案!","attend.report.i18n59");
        }

        log.debug("#####attend 月报计算：calculateRpt begin");
        List<AttendMonthRptVO> attendMonthRptVOs = iTsAttendRptCoreBusinessService.queryRpt4CalMonth(staffids, year, month, attendSchemeVO,false,null);
        log.debug("#####attend 月报计算：calculateRpt end");
        return calculateRpt(year, month, attendMonthRptVOs, timeItemVOList, extTimeItemVOList,attendSchemeVO,false);
    }

    @Override
    @Transactional
    public boolean calculateRpt(AttendPeriod period, String[] staffids, AttendSchemeVO attendSchemeVO) {
        List<TimeItemVO> extTimeItemVOList = itemService.queryTimeItemsOnlyExt(attendSchemeVO);
        Condition condition = new Condition();
        condition.addExpression("itemtype", 1);
        condition.addExpression("main_id", attendSchemeVO.getItemSchemeId());
        List<TimeItemVO> timeItemVOList = itemService.queryItemList(condition, new Sorter(Sort.Direction.ASC, "icomputeseq"));

        if (staffids == null || staffids.length == 0) {
            throw new AdderBusinessException("没有可计算人员!","attend.report.i18n26");
        }

        //查询考勤档案数据
        List<AttendMonthRptVO> attendMonthRptVOs = iTsAttendRptCoreBusinessService.queryRpt4CalMonth(staffids, period.getYear(), period.getMonth(),attendSchemeVO,false,period);
        calculateRpt_inner(period.getYear(), period.getMonth(), attendMonthRptVOs,timeItemVOList, extTimeItemVOList, attendSchemeVO,period, true,null);
        return true;
    }

    @Override
    protected void initPubEnvironmentVariables(List attendRptBaseVOs, TsAttendRptCalParamVO tsAttendRptCalParamVO) {
        if (CollectionUtils.isEmpty(attendRptBaseVOs)) {
            return;
        }
        super.initPubEnvironmentVariables(attendRptBaseVOs, tsAttendRptCalParamVO);
        List<String> stringList = FormulaUtils.getPropertyListFromBeanVO(attendRptBaseVOs, "staffId");
        String[] arrList = stringList.toArray(new String[0]);
        //初始化工作日历数据信息
        //初始化考勤期间数据
        if(tsAttendRptCalParamVO.getAttendPeriod() == null) {
            Condition condition = new Condition();
            condition.addExpression("year", tsAttendRptCalParamVO.getYear());
            condition.addExpression("month", tsAttendRptCalParamVO.getMonth());
            AttendSchemeVO attendSchemeVO = tsAttendRptCalParamVO.getAttendSchemeVO();
            condition.addExpression("main_id", attendSchemeVO.getPeriodSchemeId());
            //初始化考勤期间数据
            List<AttendPeriod> periodList = iAttendPeriodService.queryListByCondition(condition);
            if (CollectionUtils.isNotEmpty(periodList)) {
                tsAttendRptCalParamVO.setAttendPeriod(periodList.get(0));
            } else {
                throw new AdderBusinessException("考勤期间不存在!", "attend.report.i18n26");
            }
        }
        //初始化扩展变量 年假，事假
        Map<String, Map<String, Object>> extvariDatas = iTsAttendRptCoreBusinessService.queryMonthBills(arrList, tsAttendRptCalParamVO.getYear(), tsAttendRptCalParamVO.getMonth(),tsAttendRptCalParamVO.getAttendSchemeVO(),tsAttendRptCalParamVO.isTask());
        tsAttendRptCalParamVO.setExtMonthVariableDatas(extvariDatas);
        //初始化上月月报数据
        //得到上月期间
        Date predate = DateUtil.addMonth(DateUtil.StringToDate(tsAttendRptCalParamVO.getYear() + "-" + tsAttendRptCalParamVO.getMonth() + "-01"), -1);
        Map<String, AttendMonthRptVO> preMonthDatas = iTsAttendRptService.queryLastMonthByCondition(tsAttendRptCalParamVO.isTask()?null:arrList, DateUtil.getYear(predate) + "", DateUtil.getMonth(predate) + "");//获取上月月报接口
        tsAttendRptCalParamVO.setPreMonthRptDatas(preMonthDatas);

        /*//初始化本月考勤日报汇总数据
        //日报汇总数据 只包括整形和数值型
        Map<String, AttendDayRptVO> sumDayDatas = this.querySumDayByConditionDiwork(arrList, tsAttendRptCalParamVO);//获取日报汇总日报数据
        tsAttendRptCalParamVO.setSumDayRtpDatas(sumDayDatas);*/

        /**
         * 是否弹性
         */
        try {
			Map<String, Integer> elasticRuleMap = this.queryElastic_rule(tsAttendRptCalParamVO.getYear(), tsAttendRptCalParamVO.getMonth(), arrList, tsAttendRptCalParamVO.getAttendSchemeVO());
			tsAttendRptCalParamVO.setElasticRuleMap(elasticRuleMap);
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
        };
    }

    /**
     * 初始化解析公式所需变量
     *
     * @param formulaBaseVO
     * @param fillVariableMap
     * @param dayRptVO
     */
    @Override
    protected void initPerCalVariables(FormulaBaseVO formulaBaseVO, Map<String, Object> fillVariableMap, AttendRptBaseVO dayRptVO, TsAttendRptCalParamVO tsAttendRptCalParamVO) {
        super.initPerCalVariables(formulaBaseVO, fillVariableMap, dayRptVO,tsAttendRptCalParamVO);
        //初始化考勤期间数据
        fillVariableMap.put(TsMonthRptAttendPeriodVariableConvertor.ITEM_FLAG, tsAttendRptCalParamVO.getAttendPeriod());
        //获取月报汇总数据，获取上期月报数据等
        //上月考勤月报数据
        if (tsAttendRptCalParamVO.getPreMonthRptDatas() != null) {
            fillVariableMap.put(TsPreMonthRptVariableConvertor.ITEM_FLAG, tsAttendRptCalParamVO.getPreMonthRptDatas().get(dayRptVO.getStaffId()));
        }

        //获取考勤日报汇总数据
        if (tsAttendRptCalParamVO.getSumDayRtpDatas() != null) {
            fillVariableMap.put(TsDayRptSumVariableConvertor.ITEM_FLAG, tsAttendRptCalParamVO.getSumDayRtpDatas().get(dayRptVO.getStaffId()));
        }

        //获取休假数据
        //获取考勤日报汇总数据
        if (tsAttendRptCalParamVO.getExtMonthVariableDatas() != null) {
            String key = String.format("%s_%s",dayRptVO.getStaffId(),dayRptVO.getOrgId());
            fillVariableMap.put(TsLeaveVariableConvertor.ITEM_FLAG, tsAttendRptCalParamVO.getExtMonthVariableDatas().get(key));
        }

        //考勤弹性规则
        if (tsAttendRptCalParamVO.getElasticRuleMap() != null) {
            Map<String,Integer> map = new HashMap<>();
            map.put("elastic_rule", tsAttendRptCalParamVO.getElasticRuleMap().get(dayRptVO.getStaffId()));
            fillVariableMap.put(TsMonthRptElasticRuleVariableConvertor.ITEM_FLAG, map);
        }
    }

    /**
     * 计算固定列值
     *
     * @param rptVO
     */
    private void calculateRptFixItems(AttendMonthRptVO rptVO,TsAttendRptCalParamVO tsAttendRptCalParamVO) {
        //固定列（非考勤项目）
        //1、ID
        //2、staff_id
        //3、year
        //4、isnormal
        //5、billStatePass
        rptVO.setTsyear(tsAttendRptCalParamVO.getYear());
        rptVO.setTsmonth(tsAttendRptCalParamVO.getMonth());
        rptVO.setTsperiod(tsAttendRptCalParamVO.getYear() + tsAttendRptCalParamVO.getMonth());
    }

    /**
     * 获取固定更新列
     *
     * @return
     */
    @Override
    public List<TimeItemVO> getFixItems() {
        List<TimeItemVO> itemVOs = new ArrayList<>();
        TimeItemVO timeItemVO = new TimeItemVO();

        timeItemVO.setCode("staffId");
        timeItemVO.setDbcode("staff_id");
        timeItemVO.setName("staff_id");
        timeItemVO.setIdatatype(2);
        itemVOs.add(timeItemVO);
        timeItemVO = new TimeItemVO();
        timeItemVO.setCode("staffCode");
        timeItemVO.setDbcode("staff_code");
        timeItemVO.setName("staff_code");
        timeItemVO.setIdatatype(2);
        itemVOs.add(timeItemVO);
        timeItemVO = new TimeItemVO();
        timeItemVO.setCode("tsyear");
        timeItemVO.setDbcode("tsyear");
        timeItemVO.setName("tsyear");
        timeItemVO.setIdatatype(2);
        itemVOs.add(timeItemVO);
        timeItemVO = new TimeItemVO();
        timeItemVO.setCode("tsmonth");
        timeItemVO.setDbcode("tsmonth");
        timeItemVO.setName("tsmonth");
        timeItemVO.setIdatatype(2);
        itemVOs.add(timeItemVO);
        timeItemVO = new TimeItemVO();
        timeItemVO.setCode("orgId");
        timeItemVO.setDbcode("org_id");
        timeItemVO.setName("org_id");
        timeItemVO.setIdatatype(2);
        itemVOs.add(timeItemVO);
        timeItemVO = new TimeItemVO();
        timeItemVO.setCode("deptId");
        timeItemVO.setDbcode("dept_id");
        timeItemVO.setName("dept_id");
        timeItemVO.setIdatatype(2);
        itemVOs.add(timeItemVO);
        //开始时间
        timeItemVO = new TimeItemVO();
        timeItemVO.setCode("begindate");
        timeItemVO.setDbcode("begindate");
        timeItemVO.setName("begindate");
        timeItemVO.setIdatatype(4);
        itemVOs.add(timeItemVO);
        //结束时间
        timeItemVO = new TimeItemVO();
        timeItemVO.setCode("enddate");
        timeItemVO.setDbcode("enddate");
        timeItemVO.setName("enddate");
        timeItemVO.setIdatatype(4);
        itemVOs.add(timeItemVO);
        //补考勤截至天数
        timeItemVO = new TimeItemVO();
        timeItemVO.setCode("limittime");
        timeItemVO.setDbcode("limittime");
        timeItemVO.setName("limittime");
        timeItemVO.setIdatatype(1);
        itemVOs.add(timeItemVO);
        return itemVOs;
    }

    /**
     * 调用考勤月报服务删除 主子表
     *
     * @param delIds
     */
    @Transactional
    public void deleteRpts(List<String> delIds) {
        //调用考勤日报服务删除 主子表
        attendRptCalDao.batchDel4Mon(delIds);
        attendRptCalDao.batchDel4RptBByPid(delIds);
    }

    /**
     * 调用考勤日报服务删除 主子表
     *
     * @param delIds delIds
     * @param staffIds staffIds
     */
    @Transactional
    @Override
    public void deleteRpts(List<String> delIds, List<String> staffIds,String year,String month,List<String> deptIds,boolean isAuditCal) {
        //调用考勤日报服务删除 主子表
        List<String> allids = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(staffIds) && CollectionUtils.isNotEmpty(deptIds)) {
            List<String> ids = isAuditCal ? attendRptCalDao.queryMonRptByOrgCondition(staffIds,year,month,deptIds) : getMonRptIds(staffIds,year,month,deptIds);
            if (CollectionUtils.isNotEmpty(ids)) {
                allids.addAll(ids);
            }
        }

        if (CollectionUtils.isNotEmpty(delIds)) {
            for (String id : delIds) {
                if(!allids.contains(id)) {
                    allids.add(id);
                }
            }
        }

        //不用二次查询月报，delIds已经包含了。
        if (CollectionUtils.isNotEmpty(allids)) {
            attendRptCalDao.batchDel4Mon(allids);
            attendRptCalDao.batchDel4RptBByPid(allids);
        }
    }

    /***
     * 查询月报id数据
     * @param staffIds
     * @param year
     * @param month
     * @param deptIds
     * @return
     */
    private List<String> getMonRptIds(List<String> staffIds,String year,String month,List<String> deptIds){
        if(deptIds.size() < 200){
            return attendRptCalDao.queryMonRptByCondition(staffIds,year,month,deptIds);
        }
        Condition cond = new Condition();
        cond.addExpression("dept_id",deptIds, Operator.IN);
        cond.addExpression("staff_id",staffIds, Operator.IN);
        cond.addExpression("tsyear",year);
        cond.addExpression("tsmonth",month);
        return attendRptCalDao.queryMonRptTempTable(cond);
    }

    //@Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public AttendRptCalResult calculateRpt(AttendRptParams params) {
        List<AttendRptParams> paramsList = getAttendRptParamsByPeriod(params);
        AttendRptCalResult result = new AttendRptCalResult();
        for (AttendRptParams p : paramsList) {
            List<AttendMonthRptVO> attendMonthRptVOs = iTsAttendRptCoreBusinessService.queryRpt4CalMonth(p);
            result = calculateRpt_inner(p.getYear(), p.getMonth(), attendMonthRptVOs,p.getListItemVOs(), p.getListExtItemVOs(),
                    p.getAttendSchemeVO(),p.getAttendPeriod(),false,p.getLogId());
        }
        return result;
    }

    List<AttendRptParams> getAttendRptParamsByPeriod(AttendRptParams params){
        if(CollectionUtils.isEmpty(params.getAllPeriodListForMonthRpt())){
            return Arrays.asList(params);
        }
        List<AttendRptParams> list = new ArrayList<>();
        params.getAllPeriodListForMonthRpt().forEach(p -> {
            AttendRptParams prm = new AttendRptParams();
            prm.setLogId(params.getLogId());
            prm.setAttendSchemeVO(params.getAttendSchemeVO());
            prm.setYear(p.getYear());
            prm.setMonth(p.getMonth());
            prm.setAttendStaffList(params.getAttendStaffList());
            prm.setAttendPeriod(p);
            prm.setListItemVOs(params.getListItemVOs());
            prm.setListExtItemVOs(params.getListExtItemVOs());
            prm.setListStaffIds(params.getListStaffIds());
            list.add(prm);
        });
        return list;
    }

    /**
     * 计算扩展考勤项目数据
     *
     * @param rptVO
     * @param extTimeItemVOLists
     */
    private void calculateExtRptItem(AttendMonthRptVO rptVO, List<TimeItemVO> extTimeItemVOLists,TsAttendRptCalParamVO tsAttendRptCalParamVO) {
        String staffID = rptVO.getStaffId();
        Map<String, Map<String, Object>> extDataMap = tsAttendRptCalParamVO.getExtMonthVariableDatas();
        //log.debug("###attend-calculateExtRptItem-extDataMap==={},,,extTimeItemVOLists==={},staffid==={},orgid==={}",extDataMap,JSONObject.toJSONString(extTimeItemVOLists),rptVO.getStaffId(),rptVO.getOrgId());
        if (MapUtils.isNotEmpty(extDataMap)) {
            Map<String, Object> staffExtDataMap = extDataMap.get(String.format("%s_%s",staffID,rptVO.getOrgId()));
            AttendRptBVO attendRptBVO;
            List<AttendRptBVO> attendRptBVOs = new ArrayList<>();
            if (MapUtils.isNotEmpty(staffExtDataMap)) {
                //得到某人某月的扩展数据 如果没有，则直接返回
//                if (extDayDatas == null || extDayDatas.size() == 0) {
//                    rptVO.setAttendRptBVOList(null);
//                    return;
//                }
                if (CollectionUtils.isNotEmpty(extTimeItemVOLists)) {
                    for (TimeItemVO timeItemVO : extTimeItemVOLists) {
                        attendRptBVO = new AttendRptBVO();
                        attendRptBVO.setPkTimeitem(timeItemVO.getId());
                        Object value = staffExtDataMap.get(timeItemVO.getId());
                        Double doubleValue = 0.0;
                        if (value != null) {
                            if (value instanceof Double) {
                                doubleValue = (Double) value;
                            } else if (value instanceof BigDecimal) {
                                doubleValue = ((BigDecimal) value).doubleValue();
                            } else if (value instanceof String) {
                                doubleValue = Double.parseDouble(value + "");
                            } else {
                                doubleValue = Double.parseDouble(value.toString());
                            }
                        }
                        attendRptBVO.setHournum(new BigDecimal(doubleValue + ""));
                        attendRptBVO.setReportType(TsMDRptEnum.MON.getIndex());
                        attendRptBVO.setItemtype(timeItemVO.getSource());
                        attendRptBVOs.add(attendRptBVO);

                    }
                    rptVO.setAttendRptBVOList(attendRptBVOs);
                }
            }
        }
    }

    @Override
    public void setFormulaFlag(String flag) {
    }

    /**
     * 批量插入方法
     *
     * @param attendRptVOList - 考勤月报数据
     * @param timeItemVOList  - 考勤项目列
     * @param fixItemVOs      - 固定更新列
     */
    @Transactional
    @Override
    public void batchInsertDayRptVos(List<AttendMonthRptVO> attendRptVOList, List<TimeItemVO> timeItemVOList, List<TimeItemVO> fixItemVOs) {
        //固定列（非考勤项目）
        fixItemVOs.addAll(timeItemVOList);
        Map<String, String> attrs = new LinkedHashMap<>();
        //设置固定
        attrs.put("id", "id");
        //增加租户ID信息
        attrs.put("tenantid", "tenantid");
        String codeKey;
        for (TimeItemVO timeItemVO : fixItemVOs) {
            if(timeItemVO.getIdatatype() == null) {
                codeKey = timeItemVO.getCode();
            }else if (timeItemVO.getIdatatype() == 0) {
                codeKey = timeItemVO.getCode() + ":INTEGER";
            } else if (timeItemVO.getIdatatype() == 1) {
                codeKey = timeItemVO.getCode() + ":DECIMAL";
            } else if (timeItemVO.getIdatatype() == 3) {
                codeKey = timeItemVO.getCode() + ":BOOLEAN";
            } else if (timeItemVO.getIdatatype() == 4) {
                codeKey = timeItemVO.getCode() + ":TIMESTAMP";
            } else {
                codeKey = timeItemVO.getCode();
            }
            attrs.put(codeKey, timeItemVO.getDbcode().toLowerCase());
        }
        if(MapUtils.isEmpty(attrs)){
            return;
        }
        //需要插入的子表数据
        List<AttendRptBVO> rptBVOs = new ArrayList<>();
        List<AttendMonthRptVO> rptVOs = new ArrayList<>();
        for (AttendMonthRptVO monthRptVO : attendRptVOList) {
            rptVOs.add(monthRptVO);
            monthRptVO.setTenantid(AppContext.getContext().getScope());
            String id = (String)IDHelper.nextId();
            monthRptVO.setId(id);
            //处理动态项目（子表）
            processRptBVOs(rptBVOs, monthRptVO, id);
            if (rptBVOs.size() >= 2000) {
                genericService.insertByBatch(rptBVOs);
                rptBVOs.clear();
            }
        }
        if (CollectionUtils.isNotEmpty(rptBVOs)) {
            genericService.insertByBatch(rptBVOs);
        }
        if(CollectionUtils.isNotEmpty(rptVOs)){
            insertByBatch(rptVOs, attrs);
        }
    }

    /**
     * @描述   :  获取插入列字符串
     * @参数   : 【strColnums】        :strColnums
     * @参数   : 【listListValues】    :listListValues
     * @返回值 : int
     * @日期   : 2019/9/20 17:26
     * @作者   : sunshh
     */
    public int insertByBatch(List<AttendMonthRptVO> rptVOs,Map<String, String> attrs) {
        int r;
        try {
            IGenericDao dao = BatchHelper.getBatchDao(IGenericDao.class);
            Iterator var4 = rptVOs.iterator();
            List list = new LinkedList();
            while(var4.hasNext()) {
                AttendMonthRptVO e = (AttendMonthRptVO) var4.next();
                list.add(e);
                dao.insertBatchWithAttrs(list,attrs);
                list.clear();
            }
        } finally {
            r = BatchHelper.flush()[0];
        }
        return r;
    }

    private void processRptBVOs(List<AttendRptBVO> needAddBVOs, AttendRptBaseVO rptVO, String pid) {
        //处理动态项目（子表）
        List<AttendRptBVO> attendRptBVOs = rptVO.getAttendRptBVOList();
        if (CollectionUtils.isNotEmpty(attendRptBVOs)) {
            for (AttendRptBVO attendRptBVO : attendRptBVOs) {
                attendRptBVO.setPkAttendrpt(pid);
                attendRptBVO.setTenantid(AppContext.getContext().getScope());
            }
            needAddBVOs.addAll(attendRptBVOs);
        }
    }

    @Override
    public Map<String, Integer> queryElastic_rule(String year, String month, String[] staff_ids, AttendSchemeVO attendSchemeVO) {
    	Map<String, Integer> result = new HashMap<String, Integer>();
    	boolean isSenior = itimeCommonService.isSeniorVersion();
		if (isSenior) {
			for(String staff_id:staff_ids){
				result.put(staff_id, TimeConst.ELASTIC_RULE_NO);
			}
			return result;
		}
        // 查这个人这个月的排班,根据排班的校验，如果有一个班次是按月弹则他当月所有的班都是按月弹性班
        Condition periodCond = new Condition();
        periodCond.addExpression("year", year, "=");
        periodCond.addExpression("month", month, "=");
        periodCond.addExpression("main_id", attendSchemeVO.getPeriodSchemeId());
        List<AttendPeriod> periods = iAttendPeriodService.queryListByCondition(periodCond);//正常只会查出来一条
        if (CollectionUtils.isEmpty(periods)) {
            return null;
        }
        //查这个人当月的排班
        Date begindate = periods.get(0).getBegindate();
        Date enddate = periods.get(0).getEnddate();
        Condition calendar = new Condition();
        calendar.addExpression("calendar", DateUtil.DateToString(begindate, DateStyle.YYYY_MM_DD), ">=");
        calendar.addExpression("calendar", DateUtil.DateToString(enddate, DateStyle.YYYY_MM_DD), "<=");
        calendar.addExpression("staff_id", staff_ids, "in");
        List<AttendCalendar> staffCalendars = staffCalendarService.findList(calendar, null);
        if (CollectionUtils.isEmpty(staffCalendars)) {//没有排班
            return null;
        }
        //查出来所有的班次拼成MAP
        List<Shift> shiftList = iShiftService.queryListByConditon(null);
        Map<String, Shift> shiftMap = TimePubUtils.group2VOById(shiftList);
        if (null == shiftMap) {
            return null;
        }
        for (AttendCalendar sc : staffCalendars) {
            String staff_id = sc.getStaffId();
            String shiftId = sc.getShiftId();
            //如果map已经有这个人并且他的班也有值，他的班如果按月则全月都一样， 没必要再循环比较了，
            if (result.containsKey(staff_id) && null != result.get(staff_id)) {
                continue;
            }
            if (StringUtils.isEmpty(shiftId) || shiftId.endsWith("GXSHIFT") ) {//如果某人没有排班
                result.put(staff_id, null);
            } else {
                Shift shift = shiftMap.get(shiftId);
                Integer rule = shift.getElasticRule();
                if (null == rule) {
                    result.put(staff_id, TimeConst.ELASTIC_RULE_NO);
                } else {
                    result.put(staff_id, rule);
                }
            }
        }
        return result;
    }


    /**
     * 初始化考勤档案数据
     *
     * @param staffidList
     * @param paramVO
     */
    @Override
    protected void initTbmStaffdoc(List<String> staffidList, TsAttendRptCalParamVO paramVO) {
        Map<String, AttendStaff> attendStaffMap = iAttendStaffService.queryAttendStaffForRpt(paramVO.isTask()?null:staffidList.toArray(new String[0]), paramVO.getBeginDate(), paramVO.getEndDate(),false);
        paramVO.setTsPsndocMap(attendStaffMap);
    }

    /**
     * 获取考勤日报根据人员ID和日期生成的集合
     * <p>
     * Diwork 修正应出勤天数汇总不正确的bug
     *
     * @param staffid   人员ID数组
     * @param paramVO 参数
     * @return
     */
    public Map<String, AttendDayRptVO> querySumDayByConditionDiwork(String[] staffid, TsAttendRptCalParamVO paramVO) {
        Map<String, AttendDayRptVO> mapResult = new HashMap<>();
        Map<String,Map<String, AttendDayRptVO>> mapStaffOrgSumInfo = iTsAttendRptService.querySumDayByCondition(paramVO.isTask() ? null : staffid, paramVO.getBeginDate(), paramVO.getEndDate(), paramVO.getAttendSchemeVO().getItemSchemeId(),paramVO.getAttendSchemeVO().getLeaveRuleId());
        if(MapUtils.isEmpty(mapStaffOrgSumInfo)) {
            MqMsgSupport.logMsg(paramVO.getLogId(),"没有相关日报数据，返回！");
            return mapResult;
        }
        //获取考勤档案人员
        Map<String, AttendStaff> attendStaffMap = paramVO.getTsPsndocMap();
        /**排班表*/
        Map<String,Map<Date,String>> calendarMap= iAttendCalendarService.queryCalendarRuleByDate(Arrays.asList(staffid), paramVO.getBeginDate(), paramVO.getEndDate());
        Map<String,Map<Date,String>> calendarMapHis= attendCalendarHistoryService.queryCalendarRuleByDate(Arrays.asList(staffid), paramVO.getBeginDate(), paramVO.getEndDate());
        AttendParm parms = new AttendParm();
        processCalendarRuleMap(parms,calendarMap,calendarMapHis);
        //排班数据
        Map<String,Map<Date,String>> calendarDatasHis = parms.getStaffCalMap();
        //规则数据
        Map<String,Map<Date,String>> ruleDatasHis = parms.getAllStaffRuleDateMap();
        // 修正应出勤天数的bug
        // 获取所有人员对应的考勤规则
        List<String> staffIds = Arrays.asList(staffid);
        //人员对应考勤规则
        Map<String, TsAttendRuleVO> ruleMap = iAttendRuleService.queryRuleByStaffId(staffIds,false);
        //所有考勤规则
        Map<String, TsAttendRuleVO> mapAllRules = getRuleInfoMap(ruleMap);
        paramVO.setMapAttendRuleVO(ruleMap);//考勤规则
        //租户工作日历Map<id,WorkingCalendar>
        Map<String, WorkingCalendar> workingCalendarMap = getMapWorkCalendar(staffIds,ruleMap);// 获取工作日历
        Map<String, Shift> shiftMap = getMapShift(); // 获取班次

        // 获取所有holiday信息
        Condition hCond = new Condition();
        SimpleDateFormat yearSdf = new SimpleDateFormat("yyyy");
        ChainLogicExpression cle = new ChainLogicExpression(true,
                new ValueExpression("year", yearSdf.format(paramVO.getBeginDate()), "="),
                new ValueExpression("year", yearSdf.format(paramVO.getEndDate()), "="));
        hCond.addExpressions(cle);
        Map<String, List<Holiday>> holidayMap = iHolidayService.queryHolidayMapByConditon(hCond);

        Date[] range = TimePubUtils.createDateArray(paramVO.getBeginDate(), paramVO.getEndDate());
//         计算每个人的应出勤工时
        TsAttendRuleVO curRuleVO;
        Map<String,Double> mapElasticShiftWorkHour = new HashMap<>();//弹性班实际工作时长
        //工作日历对应的日期类型
        Map<String,Map<Date,Integer>> mapCalendarDateType = new HashMap<>();
        for (String staffId : staffIds) {
            String staffInfo = attendStaffMap.containsKey(staffId) ? String.format("%s(%s)",attendStaffMap.get(staffId).getStaffName(),staffId) : staffId;
            curRuleVO = ruleMap.get(staffId);
            if (curRuleVO == null && !ruleDatasHis.containsKey(staffId)) {
                MqMsgSupport.logMsg(paramVO.getLogId(),String.format("%s-没有考勤规则，跳过！",staffInfo));
                continue;
            }

            //没有汇总日报数据，不生成月报
            Map<String,AttendDayRptVO> mapOrgSumInfo = mapStaffOrgSumInfo.get(staffId);
            if(MapUtils.isEmpty(mapOrgSumInfo)) {
                log.error("#####Attend-querySumDayByConditionDiwork-没有日报，跳过,logId-{},staffId-{}",paramVO.getLogId(),staffInfo);
                continue;
            }
            TsAttendRuleVO ruleOfDay;
            Shift shiftOfDay;
            for (Map.Entry<String, AttendDayRptVO> entry : mapOrgSumInfo.entrySet()) {
                int count = 0;
                double hours = 0;
                for (Date date : range) {
                    // 考勤档案要根据日报日期及人员确定考勤档案
                    String dateStr = DateUtil.DateToString(date, DateStyle.YYYY_MM_DD);
                    //根据员工ID + date
                    AttendStaff attendstaff = attendStaffMap.get(staffId + "@" + dateStr);
                    //考勤方式不考勤或者非当前循环组织，不统计日报
                    if (null == attendstaff || TimeConst.ATTEND_TYPE_NO.equals(attendstaff.getAttendType()) || !entry.getKey().equals(attendstaff.getOrgId())) {
                        continue;
                    }

                    ruleOfDay = getRuleOfDay(date,staffId,ruleDatasHis,curRuleVO,mapAllRules);
                    if (ruleOfDay == null) {
                        MqMsgSupport.logMsg(paramVO.getLogId(),String.format("%s-%s-没有对应考勤规则，跳过！",staffInfo,dateStr));
                        continue;
                    }
                    shiftOfDay = getShiftOfDay(date,staffId,calendarDatasHis,curRuleVO,shiftMap);
                    if (shiftOfDay == null) {
                        MqMsgSupport.logMsg(paramVO.getLogId(),String.format("%s-%s-没有对应班次，跳过！",staffInfo,dateStr));
                        continue;
                    }
                    //处理工作日历相关的日期类型
                    if(!mapCalendarDateType.containsKey(ruleOfDay.getWorkCalendarId())){
                        Map<Date,Integer> map = holidayHelper.getDaysTypeDiWork(Arrays.asList(range),holidayMap.get(ruleOfDay.getWorkCalendarId()),workingCalendarMap.get(ruleOfDay.getWorkCalendarId()));
                        mapCalendarDateType.put(ruleOfDay.getWorkCalendarId(),map);
                    }
                    int worktype = getWorkType(ruleOfDay, date, shiftOfDay, workingCalendarMap.get(ruleOfDay.getWorkCalendarId()), mapCalendarDateType);//日期类型
                    log.debug("####attend-querySumDayByConditionDiwork-worktype:orgId-{},staffId-{},date-{},worktype-{}",entry.getKey(), staffInfo, date, worktype);
                    if (worktype == 1) {
                        count++;
                        //处理弹性班次的工作时长
                        if (("2".equals(ruleOfDay.getShiftModeFix()) || "3".equals(ruleOfDay.getShiftModeFix())) && shiftOfDay.isIsdeductrest()) {
                            hours += getElasticShiftWorkHour(mapElasticShiftWorkHour, shiftOfDay, date);
                        } else {
                            hours += shiftOfDay.getWorkHours();
                        }
                    }
                }
                String mapKey = String.format("%s_%s",staffId,entry.getKey());
                if (mapResult.containsKey(mapKey)) {
                    mapResult.get(mapKey).setValue("workhours", hours + "");
                    mapResult.get(mapKey).setValue("workdays", count);
                } else {
                    AttendDayRptVO attendDayRptVO = entry.getValue();
                    attendDayRptVO.setValue("workhours", hours + "");
                    attendDayRptVO.setValue("workdays", count);
                    mapResult.put(mapKey, attendDayRptVO);
                }
            }
        }
        return mapResult;
    }

    private Map<String,Shift> getMapShift(){
        List<Shift> shifts = iShiftService.queryListByCondtion(null);
        Map<String, Shift> shiftMap = new HashMap<>();
        for (Shift s : shifts) {
            shiftMap.put(s.getId(), s);
        }
        return shiftMap;
    }

    private Map<String,WorkingCalendar> getMapWorkCalendar(List<String> staffIds,Map<String, TsAttendRuleVO> ruleVOMap){
        List<WorkingCalendar> workingCalendars = genericService.find(WorkingCalendar.class, null, null);
        Map<String,WorkingCalendar> mapWCData = new HashMap<>();
        for (WorkingCalendar wc : workingCalendars) {
            mapWCData.put(wc.getId(),wc);
        }
        return mapWCData;
    }

    /***
     * 处理弹性班次的工作时长
     * @param mapElasticShift
     * @param shiftVo
     * @param date
     */
    private Double getElasticShiftWorkHour(Map<String,Double> mapElasticShift,Shift shiftVo,Date date){
        if(!shiftVo.isIsdeductrest()) {
            return shiftVo.getWorkHours();
        }
        if(mapElasticShift.containsKey(shiftVo.getId())){
            return mapElasticShift.get(shiftVo.getId());
        }
        long decuctRest = ElasticShiftUtils.getShiftDeductrest(date,shiftVo);
        Double workHours = shiftVo.getWorkHours();
        if (decuctRest > 0){
            workHours = new BigDecimal((shiftVo.getWorkHours() * 3600 - decuctRest) / 3600).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
        }
        mapElasticShift.put(shiftVo.getId(),workHours);
        return workHours;
    }

    /***
     * 获取日期类型
     * @param date
     * @param holidayList
     * @param rulevo
     * @param shiftvo
     * @param workingCalendar
     * @return
     */
    private int getWorkType(Date date,List<Holiday> holidayList,TsAttendRuleVO rulevo,Shift shiftvo,WorkingCalendar workingCalendar){
        boolean isRepDate = holidayHelper.isRepDate(date, holidayList);//是否补班
        if(isRepDate) {
            return 1;
        }
        boolean isHoliday = holidayHelper.isHolidayNew(date, holidayList);
        return ElasticShiftUtils.workType(rulevo, shiftvo, isHoliday, workingCalendar, date);
    }

    /***
     * 获取日期类型
     * @param ruleVO
     * @param date
     * @param shiftVo
     * @param workingCalendar
     * @return
     */
    private int getWorkType(TsAttendRuleVO ruleVO,Date date,Shift shiftVo,WorkingCalendar workingCalendar,Map<String,Map<Date,Integer>> mapCalendarDateType){
        try {
            log.debug(String.format("####attend-querySumDayByConditionDiwork-mapCalendarDateType:%s", mapCalendarDateType));
            if (mapCalendarDateType == null) {
                return 2;
            }
            Map<Date, Integer> map = mapCalendarDateType.get(workingCalendar.getId());
            if (map == null || !map.containsKey(date)) {
                return 2;
            }
            int dataType = map.get(date);
            //如果是排班制
            if (shiftVo != null && "1".equals(ruleVO.getShiftModeFix())) {
                if (shiftVo.getId().endsWith(IAttendConst.SHIFT_GX)) {
                    return dataType == 3 ? 3 : 2;//排了休息班次，如果日期属性为节假日，返回3，否则返回公休日
                } else {
                    return 1;
                }
            }
            return dataType;
        }catch (Exception ex){
            log.error("getWorkType error: "+ex.getMessage(),ex);
            return 2;
        }
    }

    private void processCalendarRuleMap(AttendParm param,Map<String,Map<Date,String>> calendarMap,Map<String,Map<Date,String>> calendarMapHis){
        Map<String,Map<Date,String>> calendarDatas = new HashMap<>();
        Map<String,Map<Date,String>> ruleDatas = new HashMap<>();
        processCalendarRuleMap_Single(calendarDatas,ruleDatas,calendarMapHis);
        processCalendarRuleMap_Single(calendarDatas,ruleDatas,calendarMap);
        param.setStaffCalMap(calendarDatas);
        param.setAllStaffRuleDateMap(ruleDatas);
    }

    private void processCalendarRuleMap_Single(Map<String,Map<Date,String>> calendarDatas,Map<String,Map<Date,String>> ruleDatas,Map<String,Map<Date,String>> Datas) {
        if (MapUtils.isEmpty(Datas)) {
            return;
        }
        for (Map.Entry<String,Map<Date,String>> entry : Datas.entrySet()) {
            Map<Date,String> mapCalendarDate = calendarDatas.containsKey(entry.getKey()) ? calendarDatas.get(entry.getKey()) : new HashMap<>();
            Map<Date,String> mapRuleDate = ruleDatas.containsKey(entry.getKey()) ? ruleDatas.get(entry.getKey()) : new HashMap<>();
            for (Map.Entry<Date,String> entryDate : entry.getValue().entrySet()) {
                String[] infos = entryDate.getValue().split("_");
                mapCalendarDate.put(entryDate.getKey(), infos[0]);
                mapRuleDate.put(entryDate.getKey(), infos[1]);
            }
            calendarDatas.put(entry.getKey(),mapCalendarDate);
            ruleDatas.put(entry.getKey(),mapRuleDate);
        }
    }

    /***
     * 获取日期对应的规则
     * @param date
     * @param staffId
     * @param ruleDatasHis
     * @param curRule
     * @return
     */
    private TsAttendRuleVO getRuleOfDay(Date date,String staffId,Map<String,Map<Date,String>> ruleDatasHis,TsAttendRuleVO curRule,Map<String, TsAttendRuleVO> mapAllRules){
        //先从历史数据从获取
        if(ruleDatasHis.containsKey(staffId)){
            String ruleIdOfDay = ruleDatasHis.get(staffId).get(date);
            if(StringUtils.isNotBlank(ruleIdOfDay)){
                if(mapAllRules.containsKey(ruleIdOfDay)){
                    return mapAllRules.get(ruleIdOfDay);
                }else{
                    TsAttendRuleVO rule = genericService.load(TsAttendRuleVO.class,ruleIdOfDay);
                    if(rule != null) {
                        mapAllRules.put(ruleIdOfDay,rule);
                        return rule;
                    }
                }
            }
        }
        //月弹从当前规则取
        if(curRule != null && "3".equals(curRule.getShiftModeFix())){
            return curRule;
        }
        //当天或者之后日期的返回当前规则
        if(System.currentTimeMillis() < date.getTime() || DateUtils.isSameDay(new Date(),date)){
            return curRule;
        }
        return null;
    }

    /***
     * 获取日期对应的班次
     * @param date
     * @param staffId
     * @param ruleDatasHis
     * @param curRule
     * @return
     */
    private Shift getShiftOfDay(Date date,String staffId,Map<String,Map<Date,String>> calendarDatasHis,TsAttendRuleVO curRule,Map<String, Shift> shiftMap){
        //先从历史数据从获取
        if(calendarDatasHis.containsKey(staffId)){
            String shiftIdOfDay = calendarDatasHis.get(staffId).get(date);
            if(StringUtils.isNotBlank(shiftIdOfDay)){
                return shiftMap.get(shiftIdOfDay);
            }
        }
        if(curRule == null){
            return null;
        }
        //非排班制规则，取考勤规则上的班次
        if("3".equals(curRule.getShiftModeFix())){
            return shiftMap.get(curRule.getShiftId());
        }
        //当天或者之后日期的日弹固定规则返回当前规则
        boolean needShiftInRule = (System.currentTimeMillis() < date.getTime() || DateUtils.isSameDay(new Date(),date)) && !"1".equals(curRule.getShiftModeFix());
        if(needShiftInRule){
            return shiftMap.get(curRule.getShiftId());
        }
        return null;
    }

    /***
     * 获取考勤规则MAP
     * @param staffRuleMap
     * @return
     */
    private Map<String, TsAttendRuleVO> getRuleInfoMap(Map<String, TsAttendRuleVO> staffRuleMap){
        if(MapUtils.isEmpty(staffRuleMap)){
            return new HashMap<>();
        }
        Map<String, TsAttendRuleVO> allMap = new HashMap<>();
        for (TsAttendRuleVO ruleVo : staffRuleMap.values()) {
            if(ruleVo != null) {
                allMap.put(ruleVo.getId(), ruleVo);
            }
        }
        return allMap;
    }
}