package com.jpsoft.shinestar.modules.business.service.impl;

import cn.hutool.core.date.DateUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.jpsoft.shinestar.config.WxConfig;
import com.jpsoft.shinestar.modules.base.entity.*;
import com.jpsoft.shinestar.modules.base.service.*;
import com.jpsoft.shinestar.modules.business.dao.WorkScheduleAttendanceDAO;
import com.jpsoft.shinestar.modules.business.entity.RequestForLeaveInfo;
import com.jpsoft.shinestar.modules.business.entity.WorkScheduleAttendance;
import com.jpsoft.shinestar.modules.business.entity.YearRequestForLeaveInfo;
import com.jpsoft.shinestar.modules.business.service.MeetingPersonRecodeService;
import com.jpsoft.shinestar.modules.business.service.RequestForLeaveInfoService;
import com.jpsoft.shinestar.modules.business.service.WorkScheduleAttendanceService;
import com.jpsoft.shinestar.modules.business.service.YearRequestForLeaveInfoService;
import com.jpsoft.shinestar.modules.common.dto.Sort;
import com.jpsoft.shinestar.modules.common.utils.JwtUtil2;
import com.jpsoft.shinestar.modules.common.utils.StringUtils;
import com.jpsoft.shinestar.modules.common.utils.WechatMessageUtil;
import com.jpsoft.shinestar.modules.sys.entity.DataDictionary;
import com.jpsoft.shinestar.modules.sys.service.DataDictionaryService;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.joda.time.Days;
import org.joda.time.Minutes;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
@Transactional
@Component(value = "workScheduleAttendanceService")
public class WorkScheduleAttendanceServiceImpl implements WorkScheduleAttendanceService {
    @Resource(name = "workScheduleAttendanceDAO")
    private WorkScheduleAttendanceDAO workScheduleAttendanceDAO;

    @Autowired
    private ValueOperations<String, Object> valueOperations;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private DeviceInfoService deviceInfoService;

    @Autowired
    private SceneInfoService sceneInfoService;

    @Autowired
    private WorkPersonSchedulingService workPersonSchedulingService;

    @Autowired
    private WorkShiftInfoService workShiftInfoService;

    @Autowired
    private WxConfig wxConfig;

    @Value("${jwt.secret}")
    private String jwtSecret;

    @Autowired
    private DataDictionaryService dataDictionaryService;

    @Autowired
    private PersonInfoService personInfoService;

    @Autowired
    private CompanyInfoService companyInfoService;

    @Autowired
    private PersonDeviceLogService personDeviceLogService;

    @Autowired
    private HolidayCompanyService holidayCompanyService;

    @Autowired
    private RequestForLeaveInfoService requestForLeaveInfoService;

    @Autowired
    private BusinessOutService businessOutService;

    @Autowired
    private PersonPopedomService personPopedomService;

    @Autowired
    private WorkOverPersonService workOverPersonService;

    @Autowired
    private WorkOverService workOverService;

    @Autowired
    private MeetingPersonRecodeService meetingPersonRecodeService;

    @Autowired
    private YearRequestForLeaveInfoService yearRequestForLeaveInfoService;

    private  ExecutorService threadPool = Executors.newFixedThreadPool(1);

    @Override
    public WorkScheduleAttendance get(String id) {
        // TODO Auto-generated method stub
        return workScheduleAttendanceDAO.get(id);
    }

    @Override
    public int insert(WorkScheduleAttendance model) {
        // TODO Auto-generated method stub
        //model.setId(UUID.randomUUID().toString());

        return workScheduleAttendanceDAO.insert(model);
    }

    @Override
    public int update(WorkScheduleAttendance model) {
        // TODO Auto-generated method stub
        return workScheduleAttendanceDAO.update(model);
    }

    @Override
    public int delete(String id) {
        // TODO Auto-generated method stub
        return workScheduleAttendanceDAO.delete(id);
    }

    @Override
    public boolean exist(String id) {
        // TODO Auto-generated method stub
        int count = workScheduleAttendanceDAO.exist(id);

        return count > 0 ? true : false;
    }

    @Override
    public List<WorkScheduleAttendance> list() {
        // TODO Auto-generated method stub
        return workScheduleAttendanceDAO.list();
    }

    @Override
    public Page<WorkScheduleAttendance> pageSearch(Map<String, Object> searchParams, int pageNumber, int pageSize, boolean count, List<Sort> sortList) {
        Page<WorkScheduleAttendance> page = PageHelper.startPage(pageNumber, pageSize, count).doSelectPage(() -> {
            workScheduleAttendanceDAO.search(searchParams, sortList);
        });

        return page;
    }

    @Override
    public List<Date> findAttendanceDateByPersonAndMonths(Long personId, List<String> months) {
        return workScheduleAttendanceDAO.findAttendanceDateByPersonAndMonths(personId, months);
    }

    @Override
    public List<String> findShiftByPersonAndAttendanceDate(Long personId, String attendanceDate){
        return workScheduleAttendanceDAO.findShiftByPersonAndAttendanceDate(personId, attendanceDate);
    }

    @Override
    public List<WorkScheduleAttendance> findByPersonIdAndShiftIdAndAttendanceDate(Long personId, String shiftId, String attendanceDate) {
        return workScheduleAttendanceDAO.findByPersonIdAndShiftIdAndAttendanceDate(personId, shiftId, attendanceDate);
    }

    @Override
    public int findFullAttendanceCount(Long personId, List<String> months) {
        return workScheduleAttendanceDAO.findFullAttendanceCount(personId, months);
    }

    @Override
    public boolean punchIn(String deviceNo, Long personId, BigDecimal temperature, Date recordTime) {
        boolean result = false;

        try {
            DeviceInfo deviceInfo = deviceInfoService.getByDeviceNo(deviceNo);

            if (deviceInfo == null) {
                log.error("{}无对应设备信息!", deviceNo);
                return false;
            }

            workOverService.punchIn(deviceNo,personId,recordTime);
//            List<String> sceneNameList = deviceInfoService.findSceneListByDeviceNo(deviceNo);
//
//            if (!sceneNameList.contains("考勤")) {
//                log.error("设备{}未用于考勤!", deviceInfo.getDeviceNo());
//                return false;
//            }
            PersonInfo personInfo = personInfoService.get(personId);
            long minutes = Minutes.minutesBetween(new DateTime(recordTime), DateTime.now()).getMinutes();
            long maxInterval = 15;

            //todo 2.昨天及当天人员是否有排班（夜班会跨天），考勤是否在上下班时段内
//			SimpleDateFormat ymdFormat = new SimpleDateFormat("yyyy-MM-dd");
            DateTime recordDate = new DateTime(recordTime).withTimeAtStartOfDay();

            //如果是夜班，可能是前一天打卡上班，但算今天的考勤
            //如果是晚班，可能是下班在后一天，但算今天的考勤
            for (int i = -1; i <= 1; i++) {
                DateTime date = recordDate.plusDays(i);

                WorkPersonScheduling workPersonScheduling = workPersonSchedulingService.findByPersonIdSchedulingDay(personId, date.toDate());

                if (workPersonScheduling == null) {
                    log.warn("{}在{}没有排班!", personId, date.toString("yyyy-MM-dd"));
                    continue;
                }

                String shiftIds = workPersonScheduling.getShiftIds();

                String[] shiftIdArr = shiftIds.split(",");

                List<WorkShiftInfo> shiftList = new ArrayList<>();

                for (String shiftId : shiftIdArr) {
                    WorkShiftInfo workShiftInfo = workShiftInfoService.get(shiftId);

                    if (workShiftInfo != null) {
                        if (!workShiftInfo.getNumber().equals("0")) {
                            shiftList.add(workShiftInfo);
                        }
                    }
                }

                //按照上班考勤开始时间排序
                shiftList.sort(Comparator.comparing(e -> e.getWorkStartTime()));

                for (WorkShiftInfo shiftInfo : shiftList) {
                    //打上班卡
                    String beginResult = workBeginAttendance(personId, temperature, recordTime, date, workPersonScheduling, shiftInfo);

                    if (StringUtils.isNotEmpty(beginResult)) {
                        result = true;
                        //打卡成功发送模板消息
                        if (minutes <= maxInterval){
                            //1分钟之内只通知一次
                            new Thread(()->{
                                sendWorkRemindMessage(recordTime, personInfo,shiftInfo.getId(), "班次：" + shiftInfo.getName() + "上班打卡成功");
                            }).start();
                        }

                        //如果是正常考勤,则不再继续查->有可能同时满足两个班次都是正常考勤
//                        if("1".equals(beginResult)) {
//                            break;
//                        }
                    }

                    //打下班卡
                    String endResult = workEndAttendance(personId, temperature, recordTime, date, workPersonScheduling, shiftInfo);

                    if (StringUtils.isNotEmpty(endResult)) {
                        result = true;
                        //打卡成功发送模板消息
                        if (minutes <= maxInterval){
                            //1分钟之内只通知一次
                            new Thread(()->{
                                sendWorkRemindMessage(recordTime, personInfo,shiftInfo.getId(), "班次：" + shiftInfo.getName() +"下班打卡成功");
                            }).start();
                        }

                        //如果是正常考勤,则不再继续查->有可能同时满足两个班次都是正常考勤
//                        if("1".equals(endResult)) {
//                            break;
//                        }
                    }
                }

                  //跨天班次时间有交集，同时两天的班次都算考勤
//                if(result){
//                    //说明当天排班与打卡时间有交集
//                    break;
//                }
            }

            if (minutes <= maxInterval && !result){
                //当前用户不在打卡时段，但身份是中层或高层
                List<PersonPopedom> popedomList = personPopedomService.findByPersonId(personInfo.getId());

                for (PersonPopedom personPopedom : popedomList) {
                    if ("中层".equals(personPopedom.getRoleName())
                    || "高管".equals(personPopedom.getRoleName())) {
                        sendWorkRemindMessage(recordTime, personInfo,"", "打卡成功");
                        break;
                    }
                }
            }
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
        }

        return result;
    }

    private void sendWorkRemindMessage(Date recordTime, PersonInfo personInfo,String workShiftId, String content) {
        boolean absent = valueOperations.setIfAbsent("workRemind_" + personInfo.getId() + "_" + workShiftId, true, 1, TimeUnit.MINUTES);
        if (absent) {
            new Thread(() -> {
                String accessToken = JwtUtil2.createToken(jwtSecret, personInfo.getId() + "", DateTime.now().plusHours(30 * 24).toDate());
                PersonDeviceLog personDeviceLog = personDeviceLogService.findByPersonAndRecordTime(personInfo.getId(), recordTime);
                if (personDeviceLog != null) {
                    WechatMessageUtil.sendWorkRemindMessage(
                            personInfo.getOpenId(),
                            "打卡成功",
                            personInfo.getName(),
                            DateUtil.format(recordTime, "yyyy-MM-dd HH:mm:ss"),
                            content, "打卡成功！",
                            wxConfig.getPortalUrl() + "common/health/temperatureInfo?id=" + personDeviceLog.getId() + "&token=" + accessToken,
                            wxConfig.getAppId(), wxConfig.getAppSecret());
                }
            }).start();
        }
    }

    private String workBeginAttendance(Long personId, BigDecimal temperature, Date recordTime, DateTime date,
                                        WorkPersonScheduling workPersonScheduling, WorkShiftInfo shiftInfo) throws Exception {
        //todo 3.打上班卡
        SimpleDateFormat fullFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");

        WorkShiftInfo workBeginShift = null;
        String result = ""; //0-缺卡，1-正常，2-迟到，3-早退
        int minutes = 0;


        Map<String,DateTime> timeMap = parseAttendanceTime(date.toString("yyyy-MM-dd"), shiftInfo);

        DateTime startTime = timeMap.get("workStartTime");

        //上班考勤时间设置的都是当天
        DateTime attendanceTime = timeMap.get("workTime");

        DateTime endTime = timeMap.get("workEndTime");

        //打卡时间在当前班次的考勤时间范围内
        if (recordTime.compareTo(startTime.toDate()) >= 0 && recordTime.compareTo(endTime.toDate()) <= 0) {
            workBeginShift = shiftInfo;

            //上班迟一分钟算考勤
            if (recordTime.after(attendanceTime.plusMinutes(1).toDate())) {
                //上班迟到
                result = "2";
                minutes = Minutes.minutesBetween(attendanceTime, new DateTime(recordTime)).getMinutes();
            }
            else {
                //因为有可能是连班(时间段重合),所以循环到班次是正常打卡则退出
                result = "1";
                minutes = 0;
            }
        }

        if (workBeginShift != null) {
            //查询该班次是否已有上班打卡记录，只保存最早的打卡记录即可
            List<WorkScheduleAttendance> attendanceList = findByPersonScheduleIdAndClassify(workPersonScheduling.getId(), workBeginShift.getId(), "1");

            PersonInfo personInfo = personInfoService.get(personId);
            String workStatus = personInfo.getWorkStatus();

            if (attendanceList.size() > 0) {
                //重新生成考勤记录时，以前的记录则取历史工作状态
//                if(recordTime.before(DateTime.now().withTimeAtStartOfDay().toDate())) {
                    //workStatus = attendanceList.get(0).getWorkStatus();
                    //查询当天工作状态
                    workStatus = queryWorkStatus(attendanceTime, personInfo);
//                }

                //可能存在考勤机离线后没有实时传输回打卡记录,这时需要判断数据库中已有该时间段打卡时间<当前打卡时间的数量
                long count = attendanceList.stream().filter(item -> item.getRecordTime().compareTo(recordTime) < 0 && "1".equals(item.getResult())).count();

                if (count == 0) {
                    //说明当前打卡时间更早，故删除现有该时间段打卡记录
                    for (WorkScheduleAttendance workScheduleAttendance : attendanceList) {
                        workScheduleAttendance.setDelFlag(true);
                        workScheduleAttendance.setUpdateTime(new Date());

                        workScheduleAttendanceDAO.update(workScheduleAttendance);
                    }
                } else {
                    //数据库中已有该时间段打卡时间<当前打卡时间，则忽略当前考勤记录
                    return result;
                }
            }

            //如果是状态是请假，但正常打卡则工作状态算上班
            if("4".equals(workStatus)){
                workStatus = "1";
            }

            //记录上班打卡
            saveWorkAttendance(personId, temperature, recordTime, date.toString("yyyy-MM-dd"), workPersonScheduling.getId(),
                    workBeginShift.getId(), result, minutes, "1", workStatus);
        }

        return result;
    }

    private String queryWorkStatus(DateTime attendanceTime, PersonInfo personInfo) {
        String workStatus = "1";

        List<BusinessOut> businessOutList = businessOutService.findByPersonIdAndDate(personInfo.getId(), attendanceTime.toDate());

        if (businessOutList.size()>0){
            workStatus = "3";
        }
        else {
            //查询当天是否请假
            List<RequestForLeaveInfo> requestForLeaveList = requestForLeaveInfoService.findByPersonIdAndAttendanceDate(personInfo.getId(), attendanceTime.toDate());

            if (requestForLeaveList.size() > 0) {
                workStatus = "4";
            }
        }

        return workStatus;
    }

    private String workEndAttendance(Long personId, BigDecimal temperature, Date recordTime, DateTime date,
                                      WorkPersonScheduling workPersonScheduling, WorkShiftInfo shiftInfo) throws Exception {
        //todo 3.打下班卡
        WorkShiftInfo workEndShift = null;
        String result = ""; //0-缺卡，1-正常，2-迟到，3-早退
        int minutes = 0;

        Map<String,DateTime> timeMap = parseAttendanceTime(date.toString("yyyy-MM-dd"), shiftInfo);

        DateTime startTime = timeMap.get("closingStartTime");

        //上班考勤时间设置的都是当天
        DateTime attendanceTime = timeMap.get("closingTime");

        DateTime endTime = timeMap.get("closingEndTime");

        //判断当前用户下班后是否被安排了加班，如果是则顺延考勤结束时间
        WorkOverPerson workOverPerson = workOverPersonService.findOneByPersonIdAndDate(personId,attendanceTime.toDate());

        WorkOver workOver = null;

        if(workOverPerson!=null){
            //说明下班后被安排了加班
             workOver = workOverService.get(workOverPerson.getWorkOverId());

            if(workOver!=null && workOver.getHours()!=null) {
                endTime = endTime.plusHours(workOver.getHours());
            }
        }

        //开会时如果下班则考勤截止时间顺延
        Map<String,Object> meetingMap = meetingPersonRecodeService.findByPersonIdAndDate(personId,attendanceTime.toDate());

        if(meetingMap!=null) {
            DateTime meetingStartTime = new DateTime(meetingMap.get("startTime"));
            DateTime meetingEndTime = new DateTime(meetingMap.get("endTime"));

            endTime = endTime.plusMinutes(Minutes.minutesBetween(meetingStartTime, meetingEndTime).getMinutes());
        }

        //下班结束时间延迟1分钟
        endTime = endTime.plusMinutes(1);

        if (recordTime.compareTo(startTime.toDate()) >= 0 && recordTime.compareTo(endTime.toDate()) <= 0) {
            workEndShift = shiftInfo;

            if (recordTime.before(attendanceTime.toDate())) {
                //上班早退
                result = "3";
                minutes = Minutes.minutesBetween(new DateTime(recordTime), attendanceTime).getMinutes();
            }
            else{
                //因为有可能是连班(时间段重合),所以循环到班次是正常打卡则退出
                result = "1";
                minutes = 0;
            }
        }

        if (workEndShift != null) {
            //记录加班人员加班完成时间->改在加班打卡逻辑中执行
//            if(workOverPerson!=null && workOver!=null && "1".equals(workOver.getStatus())){
//                workOverPerson.setUpdateTime(recordTime);
//                workOverPerson.setFinishTime(recordTime);
//                Integer minuteLong = (int) DateUtil.between(workOver.getStartTime(),recordTime, DateUnit.MINUTE);
//                workOverPerson.setDuration(minuteLong);
//
//                int intHours = minuteLong / 60;
//                int intMinutes = minuteLong % 60;
//                workOverPerson.setDurationStr(intHours+"小时" + intMinutes + "分钟");
//                workOverPersonService.update(workOverPerson);
//            }

            //查询该班次是否已有下班班打卡记录，打卡本身是按照升序传输
            List<WorkScheduleAttendance> attendanceList = findByPersonScheduleIdAndClassify(workPersonScheduling.getId(), workEndShift.getId(), "2");

            //记录下班打卡
            PersonInfo personInfo = personInfoService.get(personId);
            String workStatus = personInfo.getWorkStatus();

            //重新生成考勤记录时，以前的记录则取历史工作状态
//            if(recordTime.before(DateTime.now().withTimeAtStartOfDay().toDate())){
                workStatus = queryWorkStatus(attendanceTime,personInfo);
//            }

            if (attendanceList.size() > 0) {
                //可能存在考勤机离线后没有实时传输回打卡记录,这时需要判断数据库中已有该时间段打卡时间<当前打卡时间 & 打卡状态是1 的数量
                long count = attendanceList.stream().filter(item -> item.getRecordTime().compareTo(recordTime) > 0 && "1".equals(item.getResult())).count();

                if (count == 0) {
                    //将之前打卡记录删除
                    for (WorkScheduleAttendance workScheduleAttendance : attendanceList) {
                        workScheduleAttendance.setDelFlag(true);
                        workScheduleAttendance.setUpdateTime(new Date());

                        workScheduleAttendanceDAO.update(workScheduleAttendance);
                    }
                }
                else {
                    //数据库中已有该时间段打卡时间>当前打卡时间，则忽略当前考勤记录
                    return result;
                }
            }

            //如果是状态是请假，但正常打卡则工作状态算上班
            if("4".equals(workStatus)){
                workStatus = "1";
            }

            saveWorkAttendance(personId, temperature, recordTime, date.toString("yyyy-MM-dd"), workPersonScheduling.getId(),
                    workEndShift.getId(), result, minutes, "2", workStatus,workOverPerson);

            //计算本班次上班时间

            //查询当前下班的班次是否与其它班次有连班，如果有则将之前连班最早的上班打卡到现在下班打卡之间的班次都算正常上下
//            workJoin(personId, temperature, date, workPersonScheduling, shiftList, fullFormat, workEndShift, endIndex, personInfo);
        }

        return result;
    }

    /**
     * 处理连班
     *
     * @param personId
     * @param temperature
     * @param date
     * @param workPersonScheduling
     * @param shiftList
     * @param fullFormat
     * @param workEndShift
     * @param endIndex
     * @param personInfo
     * @throws ParseException
     */
    private void workJoin(Long personId, BigDecimal temperature, DateTime date, WorkPersonScheduling workPersonScheduling, List<WorkShiftInfo> shiftList, SimpleDateFormat fullFormat, WorkShiftInfo workEndShift, int endIndex, PersonInfo personInfo) throws ParseException {
        WorkShiftInfo currentShift = workEndShift;

        int startIndex = endIndex;

        for (int i = endIndex - 1; i >= 0; i--) {
            WorkShiftInfo prevShift = shiftList.get(i);

            //上一个班次的下班时间是当前班次的上班时间
            if (prevShift.getClosingTime().equals(currentShift.getWorkTime())) {
                currentShift = prevShift;
                startIndex--;
            }
        }

        boolean hasWorkStart = false;

        for (int i = startIndex; i <= endIndex; i++) {
            WorkShiftInfo shiftInfo = shiftList.get(i);

            if (!hasWorkStart) {
                //当前连班班次有上班打卡，则将剩余班次都算正常上下班
                List<WorkScheduleAttendance> attendanceList1 = findByPersonScheduleIdAndClassify(workPersonScheduling.getId(), shiftInfo.getId(), "1");

                if (attendanceList1.size() > 0) {
                    hasWorkStart = true;
                }
            }

            if (hasWorkStart) {
                //连班上班记录
                List<WorkScheduleAttendance> attendanceList1 = findByPersonScheduleIdAndClassify(workPersonScheduling.getId(), shiftInfo.getId(), "1");

                if (attendanceList1.size() == 0) {
                    //补正常上班记录
                    DateTime startTime = new DateTime(fullFormat.parse(date.toString("yyyy-MM-dd") + " " + shiftInfo.getWorkStartTime()));
                    DateTime attendanceTime = new DateTime(fullFormat.parse(date.toString("yyyy-MM-dd") + " " + shiftInfo.getWorkTime()));

                    if (startTime.compareTo(attendanceTime) > 0) {
                        attendanceTime = attendanceTime.plusDays(1);
                    }

                    saveWorkAttendance(personId, temperature, attendanceTime.toDate(),
                            date.toString("yyyy-MM-dd"), workPersonScheduling.getId(), shiftInfo.getId(), "1", 0, "1", personInfo.getWorkStatus());
                }

                //连班下班记录
                List<WorkScheduleAttendance> attendanceList2 = findByPersonScheduleIdAndClassify(workPersonScheduling.getId(), shiftInfo.getId(), "2");

                if (attendanceList2.size() == 0) {
                    //补正常下班记录
                    DateTime startTime = new DateTime(fullFormat.parse(date.toString("yyyy-MM-dd") + " " + shiftInfo.getClosingStartTime()));
                    DateTime attendanceTime = new DateTime(fullFormat.parse(date.toString("yyyy-MM-dd") + " " + shiftInfo.getClosingTime()));

                    if (startTime.compareTo(attendanceTime) > 0) {
                        attendanceTime = attendanceTime.plusDays(1);
                    }

                    saveWorkAttendance(personId, temperature, attendanceTime.toDate(), date.toString("yyyy-MM-dd"), workPersonScheduling.getId(), shiftInfo.getId(), "1", 0, "2", personInfo.getWorkStatus());
                }
            }
        }
    }

    private void saveWorkAttendance(Long personId, BigDecimal temperature, Date recordTime, String attendanceDate,
                                             String personScheduleId, String workShiftId, String result, Integer minutes,
                                             String classify, String workStatus){

        saveWorkAttendance(personId,temperature,recordTime,attendanceDate,personScheduleId,workShiftId,result,minutes,classify,workStatus,null);
    }
    private void saveWorkAttendance(Long personId, BigDecimal temperature, Date recordTime, String attendanceDate,
                                    String personScheduleId, String workShiftId, String result, Integer minutes,
                                    String classify, String workStatus, WorkOverPerson workOverPerson) {
        long offsetMinute = 0;
        int workMinutes = 0;

        String key = "wsa_" + personScheduleId + "_" + workShiftId + "_" + classify;

        try {
            boolean absent = valueOperations.setIfAbsent(key,true,1,TimeUnit.MINUTES);

            if(absent) {
                WorkShiftInfo workShiftInfo = workShiftInfoService.get(workShiftId);

                if (workShiftInfo != null) {
                    DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm");

                    if ("1".equals(classify)) {
                        //上班
                        DateTime workTime = DateTime.parse(attendanceDate + " " + workShiftInfo.getWorkTime(), formatter);
                        workTime = workTime.plusDays(workShiftInfo.getWorkDayType());

                        offsetMinute = Minutes.minutesBetween(new DateTime(recordTime), workTime).getMinutes();
                    } else {
                        //下班
                        DateTime closingTime = DateTime.parse(attendanceDate + " " + workShiftInfo.getClosingTime(), formatter);
                        closingTime = closingTime.plusDays(workShiftInfo.getClosingDayType());

                        offsetMinute = Minutes.minutesBetween(new DateTime(recordTime), closingTime).getMinutes();

                        //计算本班次上班时长（下班时间-上班时间）
                        List<WorkScheduleAttendance> onWorkList = workScheduleAttendanceDAO.findByPersonScheduleIdAndClassify(personScheduleId, workShiftId, "1");

                        if (onWorkList.size() > 0) {
                            WorkScheduleAttendance workStartAttendance = onWorkList.get(0);

                            if (StringUtils.isNotEmpty(workStartAttendance.getResult()) &&
                                    !"0".equals(workStartAttendance.getResult())) {
                                workMinutes = Minutes.minutesBetween(
                                                new DateTime(workStartAttendance.getRecordTime()),
                                                new DateTime(recordTime))
                                        .getMinutes();
                            }
                        }
                    }
                }

                WorkScheduleAttendance workEndAttendance = new WorkScheduleAttendance();

                List<WorkScheduleAttendance> attendanceList = workScheduleAttendanceDAO.findByPersonScheduleIdAndClassify(personScheduleId, workShiftId, classify);

                if (attendanceList.size() > 0) {
                    for (WorkScheduleAttendance workScheduleAttendance : attendanceList) {
                        workScheduleAttendance.setDelFlag(true);
                        workScheduleAttendance.setUpdateTime(new Date());

                        workScheduleAttendanceDAO.update(workScheduleAttendance);
                    }
                }

                workEndAttendance.setId(UUID.randomUUID().toString());
                workEndAttendance.setPersonScheduleId(personScheduleId);
                workEndAttendance.setWorkShiftId(workShiftId);
                workEndAttendance.setAttendanceDate(attendanceDate);
                workEndAttendance.setClassify(classify);
                workEndAttendance.setResult(result);
                workEndAttendance.setLateLeaveMinute(new BigDecimal(minutes));
                workEndAttendance.setWorkStatus(workStatus);
                workEndAttendance.setPersonId(personId);
                workEndAttendance.setDelFlag(false);
                workEndAttendance.setTemperature(temperature);
                workEndAttendance.setRecordTime(recordTime);
                workEndAttendance.setCreateTime(new Date());
                workEndAttendance.setOffsetMinute(new BigDecimal(offsetMinute));
                workEndAttendance.setWorkMinute(new BigDecimal(workMinutes));

                if (workOverPerson != null) {
                    workEndAttendance.setWorkOverPersonId(workOverPerson.getId());
                }

                workScheduleAttendanceDAO.insert(workEndAttendance);

                redisTemplate.delete(key);
            }
            else{
                log.warn(key + "正在处理中");
            }
        }
        catch (Exception ex){
            log.error(ex.getMessage(),ex);
            redisTemplate.delete(key);
        }
    }

    public List<WorkScheduleAttendance> findByPersonScheduleIdAndClassify(String personScheduleId, String workShiftId, String classify) {
        List<WorkScheduleAttendance> attendanceList = workScheduleAttendanceDAO.findByPersonScheduleIdAndClassify(personScheduleId, workShiftId, classify);

        return attendanceList;
    }

    @Override
    public WorkScheduleAttendance findByScheduleAttendanceId(String personScheduleId, String workShiftId, String classify) {
        WorkScheduleAttendance workScheduleAttendance = workScheduleAttendanceDAO.findByScheduleAttendanceId(personScheduleId, workShiftId, classify);

        return workScheduleAttendance;
    }

    @Override
    public WorkScheduleAttendance findLastByPersonScheduleIdAndClassify(String personScheduleId, String workShiftId, String classify) {
        WorkScheduleAttendance workScheduleAttendance = workScheduleAttendanceDAO.findLastByPersonScheduleIdAndClassify(personScheduleId, workShiftId, classify);

        return workScheduleAttendance;
    }

    private Map<String,DateTime> parseAttendanceTime(String dateStr, WorkShiftInfo workShiftInfo) throws Exception{
        //上下班时间处理
        Map<String,DateTime> timeMap = new HashMap<>();

        //上班考勤
        SimpleDateFormat fullFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");

        //上班考勤开始时间
        DateTime workStartTime = new DateTime(fullFormat.parse(dateStr + " " + workShiftInfo.getWorkStartTime()))
                                 .plusDays(workShiftInfo.getWorkDayType());

        //上班考勤时间
        DateTime workTime = new DateTime(fullFormat.parse(dateStr + " " + workShiftInfo.getWorkTime()))
                                .plusDays(workShiftInfo.getWorkDayType());

        //上班考勤结束时间
        DateTime workEndTime = new DateTime(fullFormat.parse(dateStr + " " + workShiftInfo.getWorkEndTime()))
                                .plusDays(workShiftInfo.getWorkDayType());

        // 上班时间是昨天就-1，明天就+1

        //上班考勤开始时间>上班考勤时间 如 22:00~00:00~01:00
        if (workShiftInfo.getWorkStartTime().compareTo(workShiftInfo.getWorkTime()) > 0) {
            //说明上班考勤开始时间是前一天
            workStartTime = workStartTime.plusDays(-1);
        }

        //上班考勤时间>上班考勤结束时间 目前没有该请求的排班
        if (workShiftInfo.getWorkTime().compareTo(workShiftInfo.getWorkEndTime()) > 0) {
            //说明上班考勤结束时间是后一天
            workEndTime = workEndTime.plusDays(1);
        }

        //下班考勤开始时间
        DateTime closingStartTime = new DateTime(fullFormat.parse(dateStr + " " + workShiftInfo.getClosingStartTime()))
                                        .plusDays(workShiftInfo.getClosingDayType());

        //下班考勤时间
        DateTime closingTime = new DateTime(fullFormat.parse(dateStr + " " + workShiftInfo.getClosingTime()))
                                    .plusDays(workShiftInfo.getClosingDayType());

        //下班考勤结束时间
        DateTime closingEndTime = new DateTime(fullFormat.parse(dateStr + " " + workShiftInfo.getClosingEndTime()))
                                     .plusDays(workShiftInfo.getClosingDayType());

        //下班考勤开始时间>下班考勤时间 如 23:00-00:00-02:00
        if (workShiftInfo.getClosingStartTime().compareTo(workShiftInfo.getClosingTime()) > 0) {
            //说明上班考勤开始时间是前一天
            closingStartTime = closingStartTime.plusDays(-1);
        }

        //下班考勤时间>下班考勤结束时间
        if (workShiftInfo.getClosingTime().compareTo(workShiftInfo.getClosingEndTime()) > 0) {
            //说明下班考勤结束时间是后一天
            closingEndTime = closingEndTime.plusDays(1);
        }

        timeMap.put("closingStartTime", closingStartTime);
        timeMap.put("closingTime", closingTime);
        timeMap.put("closingEndTime", closingEndTime);
        timeMap.put("workStartTime", workStartTime);
        timeMap.put("workTime", workTime);
        timeMap.put("workEndTime", workEndTime);

        return timeMap;
    }

    /**
     *
     * @param companyId 所属单位
     * @param workShiftId
     * @param personId 指定人员编号（可以为空）
     * @param personName
     * @param startDate 起始时间
     * @param endDate	结束时间
     * @param append    添加打卡记录（手动批量更新设为true，定时批量更新时设为false）
     * @return
     */
    @Override
    public int batchUpdate(String companyId,String workShiftId, Long personId,String personName, Date startDate, Date endDate, boolean append) {
//		CompanyInfo companyInfo = companyInfoService.get(companyId);

        List<PersonInfo> personInfoList = new ArrayList<>();
        //todo 1.查询当前单位下员工
        if(personId!=null){
            personInfoList.add(personInfoService.get(personId));
        }
        else {
            personInfoList = personInfoService.findByCompanyId(companyId);
        }

        DateTime dt1 = new DateTime(startDate);
        DateTime dt2 = new DateTime(endDate);
        int days = Days.daysBetween(dt1, dt2).getDays();
        Map<String, WorkShiftInfo> workShiftMap = new HashMap<>();

        int total = personInfoList.size();

        if (total==0){
            return 0;
        }

        //最多并发10个线程
        ExecutorService executor = Executors.newFixedThreadPool(10);
        CountDownLatch countDownLatch = new CountDownLatch(total);

        AtomicInteger affectCount = new AtomicInteger(0);

        for (PersonInfo personInfo : personInfoList) {
            if (personId != null && !personInfo.getId().equals(personId)) {
                //非目标人员
                countDownLatch.countDown();
                continue;
            }

            if (StringUtils.isNotEmpty(personName) && personInfo.getName().indexOf(personName)==-1){
                //非目标人员
                countDownLatch.countDown();
                continue;
            }

            executor.submit(()->{
                try {
                    for (int i = 0; i < days; i++) {
                        DateTime current = dt1.plusDays(i);
                        String dateStr = current.withTimeAtStartOfDay().toString("yyyy-MM-dd");

                        // 查询所在部门当天是否节假日
                        boolean isHoliday = holidayCompanyService.existHoliday(companyId, dateStr);

                        if (isHoliday) {
                            continue;
                        }

                        //todo 3.查询每天排班
                        WorkPersonScheduling workPersonScheduling = workPersonSchedulingService.findByPersonIdSchedulingDay(personInfo.getId(), current.toDate());

                        if (workPersonScheduling == null) {
                            //当天没有排班，则删除当天考勤记录
                            workScheduleAttendanceDAO.deleteByPersonIdAndAttendanceDate(personInfo.getId(),dateStr);
                            continue;
                        }
                        else{
                            //删除非单天排班的班次考勤（排班变更）
                            workScheduleAttendanceDAO.deleteByPersonIdAndAttendanceDateAndExcludeSchedule(personInfo.getId(),dateStr,workPersonScheduling);

//                            try {
//                                //将补卡班次调整为新的排班班次
//                                String[] shiftIds = workPersonScheduling.getShiftIds().split(",");
//                                workScheduleAttendanceDAO.updateScheduleByPersonIdAndAttendanceDate(personInfo.getId(), dateStr, workPersonScheduling.getId(), shiftIds[0]);
//                            }
//                            catch (Exception ex){
//                                log.error(ex.getMessage(),ex);
//                            }
                        }

                        //查询班次
                        String shiftIds = workPersonScheduling.getShiftIds();

                        String[] shiftIdArr = shiftIds.split(",");

                        List<WorkShiftInfo> shiftList = new ArrayList<>();

                        for (String shiftId : shiftIdArr) {
                            if (!workShiftMap.containsKey(shiftId)) {
                                WorkShiftInfo workShiftInfo = workShiftInfoService.get(shiftId);
                                workShiftMap.put(shiftId, workShiftInfo);
                            }

                            shiftList.add(workShiftMap.get(shiftId));
                        }

                        if (workPersonScheduling != null) {
                            for (WorkShiftInfo workShiftInfo : shiftList) {
                                if (workShiftInfo.getNumber().equals("0")) {
                                    continue;
                                }

                                if(StringUtils.isNotEmpty(workShiftId) && !workShiftId.equals(workShiftInfo.getId())){
                                    //如果指定了班次，则只更新指定班次的考勤
                                    continue;
                                }

                                try {
                                    Map<String,DateTime> timeMap = parseAttendanceTime(dateStr, workShiftInfo);

                                    fixWorkAttendance(dateStr, personInfo, workPersonScheduling.getId(), workShiftInfo.getId(),
                                            timeMap.get("workStartTime"), timeMap.get("workTime"), timeMap.get("workEndTime"), "1", append);

                                    affectCount.incrementAndGet();

                                    fixWorkAttendance(dateStr, personInfo, workPersonScheduling.getId(), workShiftInfo.getId(),
                                            timeMap.get("closingStartTime"), timeMap.get("closingTime"), timeMap.get("closingEndTime"), "2", append);

                                    affectCount.incrementAndGet();
                                } catch (Exception ex) {
                                    log.error(ex.getMessage(), ex);
                                }
                            }
                        }
                    }
                }
                catch (Exception ex){
                    log.error(ex.getMessage(),ex);
                }

                countDownLatch.countDown();
            });
        }

        try {
            //最多等待5分钟
            countDownLatch.await(5, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        int result = affectCount.get();

        return result;
    }

    @Override
    public WorkScheduleAttendance findByPersonIdShiftIdAttendanceDateClassifyResult(long personId, String workShiftId, String date, String classify, String result) {
        return workScheduleAttendanceDAO.findByPersonIdShiftIdAttendanceDateClassifyResult(personId, workShiftId, date, classify, result);
    }

    @Override
    public WorkScheduleAttendance findByPersonIdRecordTimeResult(long personId, String recordTime, String result) {
        return workScheduleAttendanceDAO.findByPersonIdRecordTimeResult(personId, recordTime, result);
    }

    @Override
    public List<WorkScheduleAttendance> listSearch(Map<String, Object> searchParams, List<Sort> sortList) {
        return workScheduleAttendanceDAO.search(searchParams, sortList);
    }

    @Override
    public List<Long> findPersonIdListRecordTimeResult(String attendanceDate, String result) {
        return workScheduleAttendanceDAO.findPersonIdListRecordTimeResult(attendanceDate, result);
    }

    private void fixWorkAttendance(String dateStr,
                                   PersonInfo personInfo, String personScheduleId, String workShiftId,
                                   DateTime beginTime,DateTime attendanceTime, DateTime endTime,
                                   String classify, Boolean append) {
            List<DataDictionary> ddList = dataDictionaryService.findByCatalogName("设备用途");

            String scene = "";

            for (DataDictionary dd : ddList) {
                if (dd.getName().indexOf("考勤") != -1) {
                    scene = dd.getValue();
                    break;
                }
            }

            WorkOverPerson workOverPerson = workOverPersonService.findOneByPersonIdAndDate(personInfo.getId(),attendanceTime.toDate());
            WorkOver workOver = null;

            if(workOverPerson!=null) {
                //说明下班后被安排了加班
                workOver = workOverService.get(workOverPerson.getWorkOverId());
            }

            //加班时如果是下班则考勤截止时间顺延
            if ("2".equals(classify)){
                if(workOver!=null) {
                    if(workOver.getHours()!=null) {
                        endTime = endTime.plusHours(workOver.getHours());
                    }
                }
            }

            //开会时如果下班则考勤截止时间顺延
            Map<String,Object> meetingMap = meetingPersonRecodeService.findByPersonIdAndDate(personInfo.getId(),attendanceTime.toDate());

            if ("2".equals(classify)) {
                if(meetingMap!=null) {
                    DateTime meetingStartTime = new DateTime(meetingMap.get("startTime"));
                    DateTime meetingEndTime = new DateTime(meetingMap.get("endTime"));

                    endTime = endTime.plusMinutes(Minutes.minutesBetween(meetingStartTime, meetingEndTime).getMinutes());
                }
            }

            //查询用途为考勤的设备的打卡记录(必须是升序，否则下班不是记录最迟)
            List<PersonDeviceLog> personDeviceLogs = personDeviceLogService.findByPersonAndDateAndScene(personInfo.getId(),
                    beginTime.toDate(), endTime.toDate(), scene);
//            List<PersonDeviceLog> personDeviceLogs = personDeviceLogService.findByPersonAndDate(personInfo.getId(), beginTime, endTime);

            if (personDeviceLogs.size() > 0) {
                //需要补充打卡记录（手动批量更新设为true，定时批量更新时设为false）
                if (append) {
                    PersonDeviceLog personDeviceLog = null;

                    if (classify.equals("1")) {
                        //有上班打卡记录，取最早记录记录考勤
                        personDeviceLog = personDeviceLogs.get(0);
                    } else {//有上班打卡记录，取最迟记录记录考勤
                        personDeviceLog = personDeviceLogs.get(personDeviceLogs.size() - 1);
                    }

                    punchIn(personDeviceLog.getDeviceNo(), personDeviceLog.getPersonId(),
                            personDeviceLog.getTemperature(), personDeviceLog.getRecordTime());
                }
            } else {
                //当前时间>考勤结束时间，才算缺卡
                if (DateTime.now().compareTo(endTime) > 0) {
                    //先查是否有出差，再查是否有请假，这样可以避免中午12点同时满足两种情况
                    List<BusinessOut> businessOutList1 = businessOutService.findByPersonIdAndDate(personInfo.getId(), attendanceTime.toDate());

                    if (businessOutList1.size() > 0) {
                        saveWorkAttendance(personInfo.getId(), new BigDecimal(0), attendanceTime.toDate(), dateStr,
                                personScheduleId, workShiftId, "0", 0, classify, "3");
                        return;
                    } else {
                        //年假
                        List<YearRequestForLeaveInfo> yearRequestForLeaveInfoList1 = yearRequestForLeaveInfoService.findByPersonIdAndDate(personInfo.getId(), attendanceTime.toDate());

                        if(yearRequestForLeaveInfoList1.size()>0){
                            saveWorkAttendance(personInfo.getId(), new BigDecimal(0), attendanceTime.toDate(), dateStr,
                                    personScheduleId, workShiftId, "0", 0, classify, "7");
                            return;
                        }

                        //请假
                        List<RequestForLeaveInfo> requestForLeaveList1 = requestForLeaveInfoService.findByPersonIdAndAttendanceDate(personInfo.getId(), attendanceTime.toDate());

                        if (requestForLeaveList1.size() > 0) {
                            //上班请假
                            saveWorkAttendance(personInfo.getId(), new BigDecimal(0), attendanceTime.toDate(), dateStr,
                                    personScheduleId, workShiftId, "0", 0, classify, "4");
                            return;
                        }
                    }

                    //查询人员考勤时段是否有加班
                    if(workOverPerson!=null && workOver!=null){
                        if("1".equals(classify)){
                            //上班打卡考勤缺卡，则记录实际加班时间为安排加班时间
                            workOverPerson.setBeginTime(workOver.getStartTime());
                            workOverPerson.setFinishTime(workOver.getEndTime());
                            workOverPerson.setUpdateTime(new Date());

                            if (workOverPerson.getBeginTime() != null && workOverPerson.getFinishTime() != null) {
                                int minutes = (int) Minutes.minutesBetween(new DateTime(workOverPerson.getBeginTime()),
                                        new DateTime(workOverPerson.getFinishTime())).getMinutes();
                                workOverPerson.setDuration(minutes);

                                int intHours = minutes / 60;
                                int intMinutes = minutes % 60;
                                workOverPerson.setDurationStr(intHours + "小时" + intMinutes + "分钟");
                            }

                            workOverPersonService.update(workOverPerson);
                        }

                        saveWorkAttendance(personInfo.getId(), new BigDecimal(0), attendanceTime.toDate(), dateStr,
                                personScheduleId, workShiftId, "1", 0, classify, "1",workOverPerson);

                        return;
                    }

                    //当天该班次考勤是否已补卡
                    List<WorkScheduleAttendance> attendanceList = workScheduleAttendanceDAO.findByPersonScheduleIdAndClassify(personScheduleId, workShiftId, classify);

                    for (WorkScheduleAttendance workScheduleAttendance : attendanceList) {
                        if ("1".equals(workScheduleAttendance.getResult()) && StringUtils.isNotEmpty(workScheduleAttendance.getSupplementWorkPersonId())) {
                           //已补卡则不再处理
                           return;
                        }
                    }

                    //如果没有出差、请假、加班、补卡记录则记录缺卡
                    String workStatus  = personInfo.getWorkStatus();

                    if(attendanceTime.isBefore(DateTime.now().withTimeAtStartOfDay())){
                        workStatus = "1";
                    }

                    saveWorkAttendance(personInfo.getId(), new BigDecimal(0), attendanceTime.toDate(), dateStr,
                            personScheduleId, workShiftId, "0", 0, classify, workStatus);
                }
            }
    }


    @Override
    public List<WorkScheduleAttendance> findByPersonIdAndDate(Long personId, Date beginDate, Date endDate,String result ,String workStatus) {
        return workScheduleAttendanceDAO.findByPersonIdAndDate(personId, beginDate, endDate,result,workStatus);
    }

    @Override
    public int findCountByParams(Long personId, Date startDate, Date endDate, Map<String, Object> searchParams) {
        return workScheduleAttendanceDAO.findCountByParams(personId, startDate, endDate, searchParams);
    }

    @Override
    public Double findSumLMinuteByParams(Long personId, Date startDate, Date endDate, Map<String, Object> searchParams) {
        return workScheduleAttendanceDAO.findSumLMinuteByParams(personId, startDate, endDate, searchParams);
    }

    @Override
    public List<WorkScheduleAttendance> findGroupByPersonIdAndMonthsAndResult(Long personId, Date startDate, Date endDate, String result) {
        Map<String,Object> searchParams = new HashMap<>();
        searchParams.put("result",result);
        return workScheduleAttendanceDAO.findGroupByPersonIdAndMonthsAndResult(personId,startDate,endDate, searchParams);
    }


    @Override
    public BigDecimal findAbsenteeism(Long personId, Date startDate, Date endDate, String result) {
        Map<String,Object> searchParams = new HashMap<>();
        searchParams.put("result",result);
        searchParams.put("noWorkStatus","3");
        searchParams.put("noWorkStatus4","4");
        searchParams.put("noWorkStatus7","7");
        List<WorkScheduleAttendance> wsaList = workScheduleAttendanceDAO.findAbsenteeismGroupByPersonIdAndMonthsAndResult(personId,startDate,endDate, searchParams);
        BigDecimal absenteeismNum = BigDecimal.ZERO;

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

        for (WorkScheduleAttendance wsa : wsaList) {
            try {
                //查询到有缺卡可能是本人排班休息，可能先缺卡后调班为休息，但考勤未更新的情况
                WorkPersonScheduling workPersonScheduling = workPersonSchedulingService.findByPersonIdSchedulingDay(wsa.getPersonId(), sdf.parse(wsa.getAttendanceDate()));

                if(workPersonScheduling!=null) {
                    String shiftIds = workPersonScheduling.getShiftIds();

                    String[] shiftIdArr = shiftIds.split(",");

                    boolean rest = false;

                    for (String shiftId : shiftIdArr) {
                        WorkShiftInfo workShiftInfo = workShiftInfoService.get(shiftId);

                        if ("0".equals(workShiftInfo.getNumber())) {
                            rest = true;
                            break;
                        }
                    }

                    if (!rest && wsa.getCoefficientDays() != null) {
                        absenteeismNum = absenteeismNum.add(wsa.getCoefficientDays());
                    }
                }
            }
            catch (Exception ex){
                log.error(ex.getMessage(),ex);
            }
        }

        return absenteeismNum;
    }

    @Override
    public BigDecimal findTotalAttendance(Long personId, Date startDate, Date endDate,String result){
        Map<String,Object> searchParams = new HashMap<>();
        searchParams.put("totalResult",result);
        searchParams.put("workStatus", "1"); //只算正常上班

        List<WorkScheduleAttendance> wsaList = workScheduleAttendanceDAO.findGroupByPersonIdAndMonthsAndResult(personId,startDate,endDate,searchParams);
        BigDecimal totalAttendanceNum = BigDecimal.ZERO;
        for(WorkScheduleAttendance wsa : wsaList){
            if(wsa.getCoefficientDays() != null) {
                totalAttendanceNum = totalAttendanceNum.add(wsa.getCoefficientDays());
            }
        }

        //查询只有单次打卡的记录
        List<WorkScheduleAttendance> notWsaList = workScheduleAttendanceDAO.findNotGroupByPersonIdAndMonthsAndResult(personId,startDate,endDate,searchParams);
        BigDecimal half = new BigDecimal("0.5");
        for(WorkScheduleAttendance notWsa : notWsaList){
            if(notWsa.getCoefficientDays()!=null && notWsa.getCoefficientDays().compareTo(BigDecimal.ONE) == 0){
                //如果打卡天数是1天 则加上半天
                totalAttendanceNum = totalAttendanceNum.add(half);
            }
        }

        return totalAttendanceNum;
    }

    //出差天数
    @Override
    public BigDecimal findBusinessTripNum(Long personId, Date startDate, Date endDate,Map<String, Object> searchParams){
        List<WorkScheduleAttendance> wsaList = workScheduleAttendanceDAO.findGroupByPersonIdAndMonthsAndResult(personId,startDate,endDate,searchParams);
        BigDecimal totalAttendanceNum = BigDecimal.ZERO;
        for(WorkScheduleAttendance wsa : wsaList){
            if(wsa.getCoefficientDays() != null) {
                totalAttendanceNum = totalAttendanceNum.add(wsa.getCoefficientDays());
            }
        }
        return totalAttendanceNum;
    }

    //请假次数
    @Override
    public BigDecimal findLeaveNum(Long personId, Date startDate, Date endDate,Map<String, Object> searchParams){
        List<WorkScheduleAttendance> wsaList = workScheduleAttendanceDAO.findGroupByPersonIdAndMonthsAndResult(personId,startDate,endDate,searchParams);
        BigDecimal totalAttendanceNum = BigDecimal.ZERO;
        for (WorkScheduleAttendance wsa : wsaList) {
            if (wsa.getCoefficientDays() != null) {
                totalAttendanceNum = totalAttendanceNum.add(wsa.getCoefficientDays());
            }
        }
        return totalAttendanceNum;
    }

    @Override
    public List<WorkScheduleAttendance> numListSearch(Map<String, Object> searchParams, List<Sort> sortList) {
        return workScheduleAttendanceDAO.numListSearch(searchParams,sortList);
    }

    @Override
    public void updatePersonAttendance(Long personId, Date startDate, Date endDate) {
        DateTime startTime = new DateTime(startDate);
        DateTime endTime  =  new DateTime(endDate);

        startTime = startTime.withTimeAtStartOfDay();
        endTime = endTime.withTimeAtStartOfDay().plusDays(1);

        PersonInfo personInfo = personInfoService.get(personId);

        batchUpdate(personInfo.getCompanyId(),null,personInfo.getId(),null,startTime.toDate(),endTime.toDate(),true);
    }


    @Override
    public int findHqOvertimeByPersonIdAndSEDate(Long personId, Date startDate,Date endDate){
        int overtime = 0;
        //分别查询上班时间和下班时间
        //上班时间
        List<Integer> workTimes = workScheduleAttendanceDAO.findHqWorkTimeByPersonIdAndSEDate(personId,startDate,endDate);
        for(Integer workT : workTimes){
            if(workT - 30 > 0){
                overtime = overtime + workT - 30;
            }
        }

        //下班时间
        List<Integer> closeTimes = workScheduleAttendanceDAO.findHqCloseTimeByPersonIdAndSEDate(personId,startDate,endDate);
        for(Integer closeT : closeTimes){
            if(closeT - 30 > 0){
                overtime = overtime + closeT - 30;
            }
        }
        return overtime;
    }

    @Override
    public WorkScheduleAttendance findOneBySupplementWorkPersonId(String supplementWorkPersonId) {
        return workScheduleAttendanceDAO.findOneBySupplementWorkPersonId(supplementWorkPersonId);
    }

    @Override
    public WorkScheduleAttendance findOneByWorkOverPersonId(String workOverPersonId) {
        return workScheduleAttendanceDAO.findOneByWorkOverPersonId(workOverPersonId);
    }

    @Override
    public List<Map> statScheduleAttendanceByDate(Long personId, Date startDate, Date endDate) {
        return workScheduleAttendanceDAO.statScheduleAttendanceByDate(personId,startDate,endDate);
    }

    @Override
    public List<WorkScheduleAttendance> findEmptyWorkMinuteList(int limit) {
        return workScheduleAttendanceDAO.findEmptyWorkMinuteList(limit);
    }

    @Override
    public int updateWorkMinute(WorkScheduleAttendance wsa) {
        return workScheduleAttendanceDAO.updateWorkMinute(wsa);
    }

    @Override
    public BigDecimal findSumWorkMinuteByParams(Long personId, Date startDate, Date endDate, Map<String, Object> searchParams) {
        double workMinute = workScheduleAttendanceDAO.findSumWorkMinuteByParams(personId, startDate, endDate, searchParams);
        return new BigDecimal(workMinute);
    }

    @Override
    public List<Map> queryDuplicateRecord(Date startDate, int limit) {
        return workScheduleAttendanceDAO.queryDuplicateRecord(startDate,limit);
    }

    @Override
    public List<WorkScheduleAttendance> findByPersonIdAttendanceDateResult(Long personId, String attendanceDate, String result) {
        return workScheduleAttendanceDAO.findByPersonIdAttendanceDateResult(personId,attendanceDate,result);
    }
}