package jnpf.attendance.service.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import jnpf.attendance.mapper.AttendanceClockInResultMapper;
import jnpf.attendance.mapper.AttendanceGroupMapper;
import jnpf.attendance.mapper.AttendanceResultRollbackMapper;
import jnpf.attendance.service.AttendanceChangeService;
import jnpf.attendance.service.AttendanceClockInService;
import jnpf.attendance.service.AttendanceDailyRuleService;
import jnpf.attendance.service.AttendanceDayStatisticsService;
import jnpf.attendance.service.handle.notice.AttendanceNoticeHandler;
import jnpf.base.UserInfo;
import jnpf.constants.MessageTopicConstants;
import jnpf.entity.attendance.AttendanceClockInResult;
import jnpf.entity.attendance.AttendanceResultRollback;
import jnpf.entity.attendance.FtbAttendanceClockIn;
import jnpf.entity.attendance.FtbAttendanceDailyRule;
import jnpf.entity.workflow.ApplyAttendanceChange;
import jnpf.enums.attendance.AttendanceNoticeEnum;
import jnpf.enums.attendance.TriggerSceneEnum;
import jnpf.enums.attendance.AttendanceTypeEnum;
import jnpf.enums.attendance.ClockInStatusEnum;
import jnpf.exception.HandleException;
import jnpf.exception.QueryException;
import jnpf.model.attendance.dto.NoApprovalDto;
import jnpf.model.attendance.event.StatisticsSingleDto;
import jnpf.model.attendance.model.AttendanceChangeNoticeModel;
import jnpf.model.attendance.vo.AttendanceRuleVo;
import jnpf.model.attendance.vo.ChangeInfoVo;
import jnpf.util.*;
import jnpf.workflow.mapper.ApplyAttendanceChangeMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 考勤变更服务实现
 *
 * @author yanwenfu
 * @create 2024-11-08
 */
@Slf4j
@Service
public class AttendanceChangeServiceImpl implements AttendanceChangeService {

    @Resource
    private AttendanceClockInResultMapper attendanceClockInResultMapper;
    @Resource
    private AttendanceClockInService attendanceClockInService;
    @Resource
    private AttendanceDayStatisticsService attendanceDayStatisticsService;
    @Resource
    private AttendanceDailyRuleService attendanceDailyRuleService;
    @Resource
    private AttendanceGroupMapper attendanceGroupMapper;
    @Resource
    private AttendanceResultRollbackMapper attendanceResultRollbackMapper;
    @Resource
    private ApplyAttendanceChangeMapper applyAttendanceChangeMapper;
    @Resource
    private AttendanceNoticeHandler attendanceNoticeHandler;
    @Resource
    private RocketMQTemplate rocketMqTemplate;

    @Override
    public ChangeInfoVo getChangeInfo(String clockInResultId) throws Exception {

        AttendanceClockInResult clockInResult = attendanceClockInResultMapper.selectById(clockInResultId);
        if (null == clockInResult) {
            throw new QueryException("打卡结果不存在");
        }
        AttendanceRuleVo rule = attendanceClockInService.getAttendanceRule(clockInResult.getRuleId());
        FtbAttendanceDailyRule dailyRule = new FtbAttendanceDailyRule();
        dailyRule.setDay(rule.getDay());
        dailyRule.setInPoint(rule.getInPoint());
        dailyRule.setOutPoint(rule.getOutPoint());
        String shiftTimeStr;
        if (clockInResult.getClockInType().equals(ConstantUtil.ON_WORK)) {
            shiftTimeStr = DateDetail.getDate2Str(rule.getInPoint(), DateDetail.DF4);
        } else {
            shiftTimeStr = DateDetail.getDate2Str(rule.getOutPoint(), DateDetail.DF4);
        }
        String clockInTime = "";
        if (null != clockInResult.getClockInId()) {
            LambdaQueryWrapper<FtbAttendanceClockIn> clockInQuery = new LambdaQueryWrapper<FtbAttendanceClockIn>()
                    .eq(FtbAttendanceClockIn::getId, clockInResult.getClockInId());
            FtbAttendanceClockIn clockIn = attendanceClockInService.getOne(clockInQuery);
            if (null != clockIn) {
                clockInTime = DateDetail.getDate2Str(clockIn.getClockInTime(), DateDetail.DF9);
            }
        }
        Integer status = clockInResult.getClockInStatus();
        if (null != clockInResult.getAbsence() && clockInResult.getAbsence().equals(ConstantUtil.NUM_TRUE)) {
            status = ClockInStatusEnum.ABSENCE.getValue();
        }
        return new ChangeInfoVo(clockInResultId, shiftTimeStr, clockInResult.getClockInType(), clockInTime, status);
    }

    @Override
    public List<ChangeInfoVo> getChangeInfoList(String groupId, String userId) {
        Date end = new Date();
        DateTime start = DateUtil.beginOfDay(DateUtil.offset(end, DateField.YEAR, -1));
        return attendanceClockInResultMapper.selectUserClockInResult(groupId, userId, start, end);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void attendanceChangeNoApproval(NoApprovalDto noApprovalDto) throws Exception {

        UserInfo userInfo = UserProvider.getUser();
        AttendanceClockInResult clockInResult = attendanceClockInResultMapper.selectById(noApprovalDto.getClockInResultId());
        int count = attendanceClockInResultMapper.getReplyingCount(noApprovalDto.getClockInResultId());
        if (count > 0) {
            throw new HandleException("当前记录正在审批中，请审批结束后再重新执行操作");
        }
        changePassed(clockInResult, noApprovalDto, userInfo, userInfo.getUserId());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void attendanceChangeApproval(String taskId, String passed, UserInfo user) throws Exception {

        log.error("出勤变更审批 -> taskId: {}, passed: {}, user: {}", taskId, passed, user.getUserId());
        // 根据审批id查询审批内容
        ApplyAttendanceChange apply = applyAttendanceChangeMapper.selectById(taskId);
        if (null == apply) {
            throw new Exception("未找到审批记录");
        }
        log.info("审批内容：{}", apply);
        // 查询打卡结果记录
        LambdaQueryWrapper<AttendanceClockInResult> resultQueryWrapper = new LambdaQueryWrapper<AttendanceClockInResult>()
                .eq(AttendanceClockInResult::getId, apply.getClockInResultId())
                .eq(AttendanceClockInResult::getDeleteMark, ConstantUtil.NUM_FALSE);
        AttendanceClockInResult clockInResult = attendanceClockInResultMapper.selectOne(resultQueryWrapper);
        if (null == clockInResult) {
            throw new Exception("当天班次已变更，请联系申请人重新发起！");
        }
        FtbAttendanceDailyRule rule = attendanceDailyRuleService.getById(clockInResult.getRuleId());
        if (AttendanceTypeEnum.BUSINESS_TRIP.getCode().equals(rule.getApplyViewEnable())
                || attendanceClockInService.getOutsideCheck(new AttendanceRuleVo(rule.getApplyViewEnable(), rule.getOutStepOutType(), rule.getInStepOutType()), clockInResult.getClockInType())) {
            throw new Exception("当天班次已变更，请联系申请人重新发起！");
        }
        // 判定是否封账
        String monthDate = DateDetail.getDate2Str(rule.getDay(), DateDetail.DF15);
        Map<String, Boolean> map = attendanceDayStatisticsService.selectUserIsSeal(List.of(clockInResult.getUserId()), monthDate);
        if (map.get(clockInResult.getUserId())) {
            throw new HandleException(monthDate + "已封账，过去的考勤记录无法修改！");
        }
        Integer status = null;
        switch (passed) {
            case "0":
                status = ConstantUtil.STATUS_REFUSE;
            case "2":
                log.info("拒绝或撤销...");
                if (null == status) {
                    status = ConstantUtil.STATUS_ROLLBACK;
                }
                LambdaUpdateWrapper<AttendanceClockInResult> updateWrapper = new LambdaUpdateWrapper<AttendanceClockInResult>()
                        .set(AttendanceClockInResult::getApplyId, null)
                        .set(AttendanceClockInResult::getApplyType, null)
                        .eq(AttendanceClockInResult::getId, apply.getClockInResultId())
                        .eq(AttendanceClockInResult::getDeleteMark, ConstantUtil.NUM_FALSE);
                attendanceClockInResultMapper.update(null, updateWrapper);
                break;
            case "1":
                status = ConstantUtil.STATUS_PASS;
                changePassed(clockInResult, new NoApprovalDto(apply.getClockInResultId(), apply.getChangeType(), apply.getChangeMinute()), user, apply.getCreatorUserId());
                break;
            default:
                break;
        }
        // 更新审批记录
        LambdaUpdateWrapper<ApplyAttendanceChange> changeUpdate = new LambdaUpdateWrapper<ApplyAttendanceChange>()
                .set(ApplyAttendanceChange::getApproveUserId, user.getUserId())
                .set(ApplyAttendanceChange::getApproveUserName, user.getUserName())
                .set(ApplyAttendanceChange::getApproveTime, new Date())
                .set(ApplyAttendanceChange::getStatus, status)
                .eq(ApplyAttendanceChange::getId, taskId);
        applyAttendanceChangeMapper.update(null, changeUpdate);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void rollbackChange(String clockInResultId) throws Exception {

        AttendanceResultRollback rollback = attendanceResultRollbackMapper.selectById(clockInResultId);
        if (null == rollback) {
            throw new Exception("无可用的出勤变更记录可撤回");
        }
        // 判断当前生效结果是否绑定打卡id, 有则删除绑定的打卡
        AttendanceClockInResult result = attendanceClockInResultMapper.selectById(clockInResultId);
        if (null != result && StringUtils.isNotEmpty(result.getClockInId())) {
            attendanceClockInService.removeById(result.getClockInId());
        }
        // 如果撤回的是旷工记录, 需要判断成对的打卡是否还是缺卡
        Integer absence = rollback.getAbsence();
        // 成对的那个卡
        AttendanceClockInResult record = getAnotherRecord(rollback.getRuleId(), rollback.getClockInType());
        if (rollback.getClockInStatus().equals(ClockInStatusEnum.NO_CLOCK.getValue())) {
            // 撤回之后是缺卡
            if (null == record) {
                absence = ConstantUtil.NUM_FALSE;
            } else if (record.getClockInStatus().equals(ClockInStatusEnum.NO_CLOCK.getValue())) {
                absence = ConstantUtil.NUM_TRUE;
                attendanceClockInResultMapper.updateToAbsence(record.getId());
            } else {
                absence = ConstantUtil.NUM_FALSE;
            }
        } else {
            // 撤回之后不是缺卡
            if (null != record && record.getClockInStatus().equals(ClockInStatusEnum.NO_CLOCK.getValue())) {
                attendanceClockInResultMapper.updateAbsenceToNormal(record.getId());
            }
        }
        LambdaUpdateWrapper<AttendanceClockInResult> updateWrapper = new LambdaUpdateWrapper<AttendanceClockInResult>()
                .set(AttendanceClockInResult::getRuleId, rollback.getRuleId())
                .set(AttendanceClockInResult::getUserId, rollback.getUserId())
                .set(AttendanceClockInResult::getClockInId, rollback.getClockInId())
                .set(AttendanceClockInResult::getEffectiveTime, rollback.getEffectiveTime())
                .set(AttendanceClockInResult::getClockInType, rollback.getClockInType())
                .set(AttendanceClockInResult::getClockInStatus, rollback.getClockInStatus())
                .set(AttendanceClockInResult::getClockInKind, rollback.getClockInKind())
                .set(AttendanceClockInResult::getRepaired, rollback.getRepaired())
                .set(AttendanceClockInResult::getApplyType, rollback.getApplyType())
                .set(AttendanceClockInResult::getApplyId, rollback.getApplyId())
                .set(AttendanceClockInResult::getAbsence, absence)
                .set(AttendanceClockInResult::getAbsenceLeader, rollback.getAbsenceLeader())
                .set(AttendanceClockInResult::getLastAbsenceLeader, rollback.getLastAbsenceLeader())
                .set(AttendanceClockInResult::getRestMinute, rollback.getRestMinute())
                .set(AttendanceClockInResult::getAbnormalMinute, rollback.getAbnormalMinute())
                .set(AttendanceClockInResult::getCreatorTime, rollback.getCreatorTime())
                .set(AttendanceClockInResult::getCreatorUserId, rollback.getCreatorUserId())
                .set(AttendanceClockInResult::getLastModifyTime, rollback.getLastModifyTime())
                .set(AttendanceClockInResult::getLastModifyUserId, rollback.getLastModifyUserId())
                .set(AttendanceClockInResult::getDeleteTime, rollback.getDeleteTime())
                .set(AttendanceClockInResult::getDeleteUserId, rollback.getDeleteUserId())
                .set(AttendanceClockInResult::getDeleteMark, rollback.getDeleteMark())
                .set(AttendanceClockInResult::getTenantId, rollback.getTenantId())
                .eq(AttendanceClockInResult::getId, rollback.getId());
        attendanceClockInResultMapper.update(null, updateWrapper);
        attendanceResultRollbackMapper.deleteById(clockInResultId);
    }

    @Override
    public Integer getRollbackStatus(String clockInResultId) {

        AttendanceResultRollback rollback = attendanceResultRollbackMapper.selectById(clockInResultId);
        return null == rollback ? ConstantUtil.NUM_FALSE : ConstantUtil.NUM_TRUE;
    }

    /**
     * 变更结果通过
     * @param clockInResult 原记录
     * @param noApprovalDto 变更参数
     * @param userInfo 变更人
     */
    private void changePassed(AttendanceClockInResult clockInResult, NoApprovalDto noApprovalDto, UserInfo userInfo, String dealUser) throws Exception {

        if (null == noApprovalDto.getChangeMinute()) {
            noApprovalDto.setChangeMinute(0);
        }
        AttendanceRuleVo rule = attendanceClockInService.getAttendanceRule(clockInResult.getRuleId());
        if (rule == null) {
            log.info("未找到出勤规则");
            return;
        }
        // 判断考勤组是否被封账
        String monthDate = DateDetail.getDate2Str(rule.getDay(), DateDetail.DF15);
        Map<String, Boolean> map = attendanceDayStatisticsService.selectUserIsSeal(List.of(rule.getUserId()), monthDate);
        if (map.get(rule.getUserId())) {
            throw new HandleException(monthDate + "已封账，过去的考勤记录无法变更！");
        }
        ClockInStatusEnum statusEnum = ClockInStatusEnum.getClockInStatusEnum(noApprovalDto.getChangeType());
        if (null == statusEnum) {
            throw new Exception("变更类型异常");
        }
        // 变更撤回维护
        if (!statusEnum.equals(ClockInStatusEnum.ROLLBACK)) {
            attendanceResultRollbackMapper.deleteById(clockInResult.getId());
            AttendanceResultRollback resultRollback = JsonUtil.getJsonToBean(clockInResult, AttendanceResultRollback.class);
            resultRollback.setApplyId(null);
            resultRollback.setApplyType(null);
            attendanceResultRollbackMapper.insert(resultRollback);
        }
        // 生成新的打卡记录
        DateDetail dateDetail = new DateDetail();
        FtbAttendanceClockIn clockIn = generateClockIn(rule, clockInResult, noApprovalDto.getChangeMinute(), dateDetail);
        boolean anotherChange = false;
        switch (statusEnum) {
            case NORMAL:
                // 迟到, 早退, 缺卡, 旷工 -> 正常
                // 旷工变更为正常, 成对的另一个旷工变更为缺卡
                if (null != clockInResult.getAbsence() && clockInResult.getAbsence().equals(ConstantUtil.NUM_TRUE)) {
                    anotherChange = true;
                    clockInResult.setAbsence(ConstantUtil.NUM_FALSE);
                }
                clockInResult.setClockInStatus(ClockInStatusEnum.NORMAL.getValue());
                clockInResult.setAbnormalMinute(0);
                if (clockInResult.getClockInType().equals(ConstantUtil.ON_WORK)) {
                    clockInResult.setEffectiveTime(rule.getInPoint());
                } else {
                    clockInResult.setEffectiveTime(rule.getOutPoint());
                    // 设置打卡休息时间
                    if (null != rule.getBreakEnable() && rule.getBreakEnable().equals(ConstantUtil.NUM_TRUE)) {
                        int restMinute = DateDetail.calculateMinuteDiff(rule.getBreakStartPoint(), rule.getBreakEndPoint());
                        clockInResult.setRestMinute(restMinute);
                    }
                }
                break;
            case WORK_LATE:
                // 上班: 正常, 迟到, 缺卡, 旷工 -> 迟到
                // 旷工变更为迟到, 成对的另一个旷工变更为缺卡
                if (null != rule.getFixedMark() && rule.getFixedMark().equals(2)) {
                    throw new HandleException("划线排班打卡结果不能变更为迟到");
                }
                if (null != clockInResult.getAbsence() && clockInResult.getAbsence().equals(ConstantUtil.NUM_TRUE)) {
                    anotherChange = true;
                    clockInResult.setAbsence(ConstantUtil.NUM_FALSE);
                }
                clockInResult.setClockInStatus(ClockInStatusEnum.WORK_LATE.getValue());
                clockInResult.setAbnormalMinute(noApprovalDto.getChangeMinute() * 60);
                clockInResult.setEffectiveTime(dateDetail.addMinute(rule.getInPoint(), noApprovalDto.getChangeMinute()));
                // 设置下班的休息时间
                setRestMinute(rule, ConstantUtil.ON_WORK, clockInResult);
                break;
            case HOME_EARLY:
                // 下班: 正常, 早退, 缺卡, 旷工 -> 早退
                // 旷工变更为迟到, 成对的另一个旷工变更为缺卡
                if (null != rule.getFixedMark() && rule.getFixedMark().equals(2)) {
                    throw new HandleException("划线排班打卡结果不能变更为早退");
                }
                if (null != clockInResult.getAbsence() && clockInResult.getAbsence().equals(ConstantUtil.NUM_TRUE)) {
                    anotherChange = true;
                    clockInResult.setAbsence(ConstantUtil.NUM_FALSE);
                }
                clockInResult.setClockInStatus(ClockInStatusEnum.HOME_EARLY.getValue());
                clockInResult.setAbnormalMinute(noApprovalDto.getChangeMinute() * 60);
                clockInResult.setEffectiveTime(dateDetail.addMinute(rule.getOutPoint(), -noApprovalDto.getChangeMinute()));
                // 设置下班的休息时间
                setRestMinute(rule, ConstantUtil.OFF_WORK, clockInResult);
                break;
            case NO_CLOCK:
                // 正常, 迟到, 早退 -> 缺卡
                AttendanceClockInResult workRecord = getAnotherRecord(clockInResult.getRuleId(), clockInResult.getClockInType());
                if (null != workRecord && workRecord.getClockInStatus().equals(ClockInStatusEnum.NO_CLOCK.getValue())) {
                    workRecord.setAbsence(ConstantUtil.NUM_TRUE);
                    LambdaUpdateWrapper<AttendanceClockInResult> update = new LambdaUpdateWrapper<AttendanceClockInResult>()
                            .set(AttendanceClockInResult::getAbsence, ConstantUtil.NUM_TRUE)
                            .eq(AttendanceClockInResult::getId, workRecord.getId());
                    attendanceClockInResultMapper.update(null, update);
                    clockInResult.setAbsence(ConstantUtil.NUM_TRUE);
                }
                // 成对的打卡是缺卡, 则两个都变更为旷工
                clockInResult.setClockInStatus(ClockInStatusEnum.NO_CLOCK.getValue());
                clockInResult.setAbnormalMinute(0);
                if (clockInResult.getClockInType().equals(ConstantUtil.ON_WORK)) {
                    clockInResult.setEffectiveTime(rule.getInPoint());
                } else {
                    clockInResult.setEffectiveTime(rule.getOutPoint());
                    clockInResult.setRestMinute(0);
                }
                break;
            case ROLLBACK:
                // 撤销变更
                rollbackChange(clockInResult.getId());
                // 发布日统计数据生成事件
                StatisticsSingleDto courseEventDTO = StatisticsSingleDto.builder()
                        .tenantId(userInfo.getTenantId())
                        .groupId(rule.getGroupId())
                        .userId(clockInResult.getUserId())
                        .triggerSceneEnum(TriggerSceneEnum.SCHEDULES)
                        .day(rule.getDay())
                        .build();
                if(StrUtil.isBlank(userInfo.getTenantId()) || StrUtil.isBlank(rule.getGroupId())){
                    log.error("AttendanceChangeServiceImpl#changePassed 触发日统计租户ID或者考勤组ID为空：{}", JSON.toJSONString(courseEventDTO));
                }
                Message<StatisticsSingleDto> message = MessageBuilder.withPayload(courseEventDTO).build();
                rocketMqTemplate.syncSend(MessageTopicConstants.ATTENDANCE_STATISTICS_SINGLE_TOPIC, message, 3000L, 2);
                return;
            default:
                break;
        }
        if (anotherChange) {
            // 成对的卡取消旷工标识
            AttendanceClockInResult workRecord = getAnotherRecord(clockInResult.getRuleId(), clockInResult.getClockInType());
            if (null != workRecord) {
                attendanceClockInResultMapper.updateAbsenceToNormal(workRecord.getId());
            }
        }
        clockInResult.setApplyType(null);
        clockInResult.setApplyId(null);
        clockInResult.setLastAbsenceLeader(clockInResult.getAbsenceLeader());
        clockInResult.setClockInId(clockIn.getId());
        clockInResult.setRepaired(ConstantUtil.NUM_FALSE);
        clockInResult.setLastModifyTime(new Date());
        clockInResult.setLastModifyUserId(userInfo.getUserId());
        attendanceClockInService.save(clockIn);
        attendanceClockInResultMapper.updateById(clockInResult);
        attendanceClockInResultMapper.updateResultReplyToNull(clockInResult.getId(), dealUser);
        sendResultChangedToIm(clockInResult, userInfo.getTenantId(), userInfo.getUserId(), noApprovalDto.getChangeType());
        // 发布日统计数据生成事件
        StatisticsSingleDto courseEventDTO = StatisticsSingleDto.builder()
                .tenantId(userInfo.getTenantId())
                .groupId(rule.getGroupId())
                .userId(clockInResult.getUserId())
                .day(rule.getDay())
                .triggerSceneEnum(TriggerSceneEnum.SCHEDULES)
                .build();
        if(StrUtil.isBlank(userInfo.getTenantId()) || StrUtil.isBlank(rule.getGroupId())){
            log.error("AttendanceChangeServiceImpl#changePassed 触发日统计租户ID或者考勤组ID为空：{}", JSON.toJSONString(courseEventDTO));
        }
        Message<StatisticsSingleDto> message = MessageBuilder.withPayload(courseEventDTO).build();
        rocketMqTemplate.syncSend(MessageTopicConstants.ATTENDANCE_STATISTICS_SINGLE_TOPIC, message, 3000L, 2);
    }

    private void setRestMinute(AttendanceRuleVo rule, Integer workType, AttendanceClockInResult clockInResult) {
        // 设置打卡休息时间
        if (null != rule.getBreakEnable() && rule.getBreakEnable().equals(ConstantUtil.NUM_TRUE)) {
            // 查班次对应的上班记录
            AttendanceClockInResult workResult = getAnotherRecord(rule.getId(), workType);
            if (workType.equals(ConstantUtil.ON_WORK)) {
                // 变更上班, 处理下班休息时间
                if (null == workResult || workResult.getClockInStatus().equals(ClockInStatusEnum.NO_CLOCK.getValue())) {
                    return;
                }
                Date breakStart = clockInResult.getEffectiveTime().after(rule.getBreakStartPoint()) ? clockInResult.getEffectiveTime() : rule.getBreakStartPoint();
                Date breakEnd = workResult.getEffectiveTime().before(rule.getBreakEndPoint()) ? workResult.getEffectiveTime() : rule.getBreakEndPoint();
                int restMinute = DateDetail.calculateMinuteDiff(breakStart, breakEnd);
                LambdaUpdateWrapper<AttendanceClockInResult> updateWrapper = new LambdaUpdateWrapper<AttendanceClockInResult>()
                        .set(AttendanceClockInResult::getRestMinute, restMinute)
                        .eq(AttendanceClockInResult::getId, workResult.getId());
                attendanceClockInResultMapper.update(null, updateWrapper);
            } else {
                // 变更下班, 处理下班休息时间
                if (null != workResult) {
                    Date breakStart;
                    if (null == workResult.getEffectiveTime()) {
                        breakStart = rule.getBreakStartPoint();
                    } else {
                        breakStart = workResult.getEffectiveTime().after(rule.getBreakStartPoint()) ? workResult.getEffectiveTime() : rule.getBreakStartPoint();
                    }
                    Date breakEnd = clockInResult.getEffectiveTime().before(rule.getBreakEndPoint()) ? clockInResult.getEffectiveTime() : rule.getBreakEndPoint();
                    int restMinute = DateDetail.calculateMinuteDiff(breakStart, breakEnd);
                    clockInResult.setRestMinute(restMinute);
                }
            }
        }
    }

    private void sendResultChangedToIm(AttendanceClockInResult clockInResult, String tenantId, String handleUserId, Integer changeType) {

        AttendanceChangeNoticeModel model = new AttendanceChangeNoticeModel();
        model.setAttendanceNoticeModel(clockInResult.getUserId(), tenantId, AttendanceNoticeEnum.CHECK_RESULT_CHANGE);
        model.setHandleUserId(handleUserId);
        model.setChangeType(changeType);
        model.setRuleId(clockInResult.getRuleId());
        model.setClockInType(clockInResult.getClockInType());
        attendanceNoticeHandler.send(model);
    }

    private AttendanceClockInResult getAnotherRecord(String ruleId, Integer clockInType) {

        LambdaQueryWrapper<AttendanceClockInResult> queryWrapper = new LambdaQueryWrapper<AttendanceClockInResult>()
                .eq(AttendanceClockInResult::getRuleId, ruleId)
                .eq(AttendanceClockInResult::getClockInType, clockInType.equals(ConstantUtil.ON_WORK) ? ConstantUtil.OFF_WORK : ConstantUtil.ON_WORK)
                .eq(AttendanceClockInResult::getDeleteMark, ConstantUtil.NUM_FALSE);
        return attendanceClockInResultMapper.selectOne(queryWrapper);
    }

    private FtbAttendanceClockIn generateClockIn(AttendanceRuleVo rule, AttendanceClockInResult clockInResult, Integer changeMinute, DateDetail dateDetail) {

        FtbAttendanceClockIn clockIn = new FtbAttendanceClockIn();
        clockIn.setId(FtbUtil.getId());
        clockIn.setDay(rule.getDay());
        clockIn.setUserId(clockInResult.getUserId());
        if (changeMinute.equals(0)) {
            if (clockInResult.getClockInType().equals(ConstantUtil.ON_WORK)) {
                clockIn.setClockInTime(rule.getInPoint());
            } else {
                clockIn.setClockInTime(rule.getOutPoint());
            }
        } else {
            if (null == rule.getFixedMark() || !rule.getFixedMark().equals(2)) {
                if (clockInResult.getClockInType().equals(ConstantUtil.ON_WORK)) {
                    clockIn.setClockInTime(dateDetail.addMinute(rule.getLatePoint(), changeMinute));
                } else {
                    clockIn.setClockInTime(dateDetail.addMinute(rule.getOutPoint(), -changeMinute));
                }
            }
        }
        clockIn.setApprovalStatus(ConstantUtil.PASS_TRUE);
        clockIn.setClockInKind(ConstantUtil.KIND_NORMAL);
        return clockIn;
    }
}
