SchedulingService.java 13.2 KB
package com.ruoyi.service;

import cn.hutool.core.collection.CollectionUtil;
import com.ruoyi.common.cache.NowSchedulingCache;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.driver.mapper.DriverSchedulingMapper;
import com.ruoyi.in.domain.SignIn;
import com.ruoyi.in.mapper.SignInMapper;
import com.ruoyi.in.service.impl.SignInServiceImpl;
import com.ruoyi.pojo.DriverSignInRecommendation;
import com.ruoyi.pojo.GlobalIndex;
import com.ruoyi.domain.DriverScheduling;
import com.ruoyi.pojo.request.ReportViewRequestVo;
import com.ruoyi.pojo.response.ReportViewResponseVo;
import com.ruoyi.utils.ConstDateUtil;
import com.ruoyi.utils.ToolUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.temporal.ChronoUnit;
import java.util.*;

import static com.ruoyi.common.ConstDriverProperties.BC_TYPE_IN;
import static com.ruoyi.common.ConstDriverProperties.BC_TYPE_OUT;
import static com.ruoyi.common.ConstSignInConstSignInProperties.*;
import static com.ruoyi.common.ErrorTypeProperties.*;
import static com.ruoyi.common.ReportProperties.NOW;
import static com.ruoyi.common.SignStatusEnum.SIGN_STATUS_DELAY_ENUM;

/**
 * @author 20412
 */
@Service
public class SchedulingService {

    @Resource
    private NowSchedulingCache nowSchedulingCache;

    @Autowired
    private DriverSchedulingMapper schedulingMapper;

    @Autowired
    private SignInMapper signInMapper;


    /**
     * 获取排班信息 并排序
     *
     * @param jobCode
     * @param now
     * @return
     */
    public List<DriverScheduling> queryScheduling(String jobCode, Long now) {

        List<DriverScheduling> dto = null;
        for (int i = 0; i > -2; i--) {
            dto = nowSchedulingCache.getCacheSchedulingMapValueByHKey(ConstDateUtil.formatDate(ConstDateUtil.getTheSpecifiedNumberOfDaysOfTime(i)), jobCode);
            if (!CollectionUtil.isEmpty(dto)) {
                dto.sort(Comparator.comparing(DriverScheduling::getZdsjT));
                if (i == -1) {
                    LocalDateTime zdsjT = ConstDateUtil.getLocalDateTimeByLongTime(dto.get(dto.size() - 1).getZdsjT());
                    LocalDateTime nowTime = ConstDateUtil.getLocalDateTimeByLongTime(now);
                    long range = ChronoUnit.MINUTES.between(zdsjT, nowTime);
                    // 判定存在排班 前天的最后一次打卡时间超过2小时,排班为null
                    if (range > 120L) {
                        dto = null;
                    }
                }
                break;
            }
        }
        return dto;
    }


    /**
     * 计算当前签到签到逻辑
     *
     * @param dto
     * @param now
     * @return
     */
    public DriverSignInRecommendation computedTheCurrentClosestTimestamp(List<DriverScheduling> dto, Long now, Integer result) {
        Map<Integer, DriverSignInRecommendation> timeMap = new HashMap<>();
        for (int i = 0; i < dto.size(); i++) {
            timeMap.put(i, new DriverSignInRecommendation(dto.get(i), i));
        }
        long minDiff = Long.MAX_VALUE;
        Integer index = 0;
        // 迭代比较每个时间戳与当前时间戳的差值
        for (Integer i : timeMap.keySet()) {
            long diff = Math.abs(now - timeMap.get(i).getTimestamps());
            if (diff < minDiff) {
                minDiff = diff;
                index = i;
            }
        }
        if (index + result >= 0 && index + result < timeMap.size()) {
            index = index + result;
        }
        return timeMap.get(index);
    }

    /**
     * 更具最新的签到记录判断是否需要更新考勤。
     *
     * @param dto
     * @param signIn
     * @param globalIndex
     */
    public void computedSignInBySignIn(List<DriverScheduling> dto, SignIn signIn, GlobalIndex globalIndex) {
        // 无排班不记录不在考勤表不更新
        if (CollectionUtil.isEmpty(dto)) {
            return;
        }
        String remark = getRemark(dto, signIn, globalIndex.getIndex());
        // 更新最新的签到记录判断是否需要更新考勤
        // 记录为空直接插入记录
        if (Objects.isNull(dto.get(globalIndex.getIndex()).getSignInId()) || dto.size() == 1) {
            schedulingMapper.updateRoster(dto.get(globalIndex.getIndex()), signIn.getId(), signIn.getExType(), signIn.getCreateTime(), remark, signIn.getType(), signIn.getAlcoholFlag(), signIn.getAlcoholIntake());
            // 更新缓存
            nowSchedulingCache.updateCacheByJobCode(remark, ConstDateUtil.formatDate(dto.get(0).getScheduleDate()), globalIndex.getIndex(), signIn);
        }
        // 之前的无效
        else if (!SIGN_NO_EX_NUM.equals(dto.get(globalIndex.getIndex()).getExType())) {
            schedulingMapper.updateRoster(dto.get(globalIndex.getIndex()), signIn.getId(), signIn.getExType(), signIn.getCreateTime(), remark, signIn.getType(), signIn.getAlcoholFlag(), signIn.getAlcoholIntake());
            nowSchedulingCache.updateCacheByJobCode(remark, ConstDateUtil.formatDate(dto.get(0).getScheduleDate()), globalIndex.getIndex(), signIn);
        }
        // 之前的有效
        else {
            handlerRecord(dto, signIn, globalIndex);
        }

    }

    private String getRemark(List<DriverScheduling> dto, SignIn signIn, Integer globalIndex) {
        StringBuilder sb = new StringBuilder();
        DriverScheduling scheduling = dto.get(globalIndex);
        long date = scheduling.getBcType().equals(BC_TYPE_IN) ? scheduling.getZdsjT() : scheduling.getFcsjT();
        long nowBetween = ChronoUnit.MINUTES.between(ConstDateUtil.getLocalDateTimeByLongTime(date), ConstDateUtil.getLocalDateTimeByLongTime(signIn.getCreateTime().getTime()));
        if (SignInServiceImpl.checkTimerSign(nowBetween, scheduling.getBcType())) {
            if (nowBetween < -60L || (BC_TYPE_IN.equals(scheduling.getBcType()) && nowBetween < 0L) ) {
                sb.append(EARLY);
            } else {
                sb.append(SIGN_STATUS_DELAY_ENUM.getDescription(scheduling.getBcType()));
            }
            // 在规定时间就还原remark
        } else {
            sb.append(signIn.getRemark());
        }
        if (SIGN_ALCOHOL_EX_NUM.equals(signIn.getExType())) {
            int index = signIn.getRemark().indexOf(ALCOHOL_SIGN_IN_ERROR);
            if (index != -1) {
                sb.append("," + signIn.getRemark().substring(index));

            }
        }
        return sb.toString();
    }


    private void handlerRecord(List<DriverScheduling> dto, SignIn signIn, GlobalIndex globalIndex) {
        if (globalIndex.getIndex() == dto.size() - 1) {
            return;
        }
        long timer = 1000 * 60 * 3;
        // 有效的在三分钟内重复签到不做修改
        if (signIn.getExType().equals(SIGN_NO_EX_NUM) && (DateUtils.getNowDate().getTime() - dto.get(globalIndex.getIndex()).getSignTime().getTime()) <= timer) {
            signIn.setRemark("您已经打卡过了,请勿在3分钟内重复打卡");
            return;
        }
        //   目前也有效 -》 无需更新 || 目前无效 -》往后更新  TODO 判断时间   之前的有效 但是时间还在有效范围内打卡的话会导致排班表数据不匹配
        if (signIn.getExType().equals(SIGN_NO_EX_NUM)) {
            return;
        }
        int index = globalIndex.getIndex() + 1;
        DriverScheduling scheduling = dto.get(index);
        long date = scheduling.getBcType().equals(BC_TYPE_IN) ? scheduling.getZdsjT() : scheduling.getFcsjT();
        String prompt = getPrompt(new Date(date), scheduling.getBcType());
        signIn.setRemark((scheduling.getBcType().equals(BC_TYPE_IN) ? SIGN_OUT_TIMEOUT : SIGN_IN_TIMEOUT) + prompt);
        signIn.setRemark(signIn.getRemark().replaceFirst(",$", "。"));
        String remark = getRemark(dto, signIn, index);
        schedulingMapper.updateRoster(scheduling, signIn.getId(), signIn.getExType(), signIn.getCreateTime(), remark, signIn.getType(), signIn.getAlcoholFlag(), signIn.getAlcoholIntake());
        nowSchedulingCache.updateCacheByJobCode(remark, ConstDateUtil.formatDate(dto.get(0).getScheduleDate()), globalIndex.getIndex() + 1, signIn);
    }

    private String getPrompt(Date date, String bcType) {
        if (BC_TYPE_OUT.equals(bcType)) {
            return "请在" + ConstDateUtil.formatDate("HH:mm", date) + "前后一小时内打卡。";
        } else {
            // 正两小时 负 1小时
            LocalDateTime time = ConstDateUtil.stringTransformLocalDateTime(ConstDateUtil.formatDate("yyyy-MM-dd HH:mm:ss", date), "yyyy-MM-dd HH:mm:ss");
            LocalDateTime addHours = time.plusHours(2);
            LocalDateTime hours = time.plusHours(-1);
            return "请在" + ConstDateUtil.formatDate("HH:mm", hours) + "到"
                    + ConstDateUtil.formatDate("HH:mm", addHours) + "之间打卡";
        }
    }

    public List<ReportViewResponseVo> queryReportTableResponseVo(ReportViewRequestVo requestVo, HttpServletResponse response) {
        // 处理天
        if (requestVo.getExportFlag().equals(NOW)) {
            return getDayReportTableResponseVo(requestVo);
        }
        return null;
    }


    private List<ReportViewResponseVo> getDayReportTableResponseVo(ReportViewRequestVo vo) {
        // 签到数据
        List<DriverScheduling> toDay = schedulingMapper.queryToDay(vo.getDate(), vo.getName(), vo.getJobCode(), vo.getLineName());
        toDay.sort(Comparator.comparing(DriverScheduling::getFcsjT));
        // 转换日期 + jobCode为key
        Map<String, List<DriverScheduling>> orangeMap = new HashMap<>(1200);
        transformMapByDriverScheduling(vo, toDay, orangeMap);
        Map<String, ReportViewResponseVo> resultMap = new HashMap<>(1200);
        for (String key : orangeMap.keySet()) {
            List<DriverScheduling> list = orangeMap.get(key);
            // 如果有选超时过滤的话过滤
            if (filterReportVoList(vo.getStage(), list)) {
                continue;
            }
            ReportViewResponseVo vo1 = new ReportViewResponseVo();
            handlerScheduling(list, vo1);
            resultMap.put(key, vo1);
        }
        return new ArrayList<>(resultMap.values());
    }

    private static void transformMapByDriverScheduling(ReportViewRequestVo vo, List<DriverScheduling> toDay, Map<String, List<DriverScheduling>> orangeMap) {
        for (DriverScheduling scheduling : toDay) {
            String key = vo.getDate() + scheduling.getJobCode();
            if (Objects.isNull(orangeMap.get(key))) {
                ToolUtils.updateReport(scheduling);
                orangeMap.put(key, new ArrayList<>(Arrays.asList(scheduling)));
            } else {
                orangeMap.get(key).add(scheduling);
            }
        }
    }

    private boolean filterReportVoList(Integer stage, List<DriverScheduling> list) {
        if (!stage.equals(0)) {
            return handleReportScrollViewTable(stage, list);
        }
        return false;
    }

    private boolean handleReportScrollViewTable(Integer stage, List<DriverScheduling> list) {
        if (list.size() < stage) {
            return true;
        }
        DriverScheduling scheduling = list.get(stage - 1);
        // 获取两个 Date 对象之间的时间差(以毫秒为单位)
        long signTime = scheduling.getSignTime() == null ? System.currentTimeMillis() : scheduling.getSignTime().getTime();
        long timeDifferenceInMillis = signTime - (scheduling.getBcType().equals(BC_TYPE_OUT) ? scheduling.getFcsjT() : scheduling.getZdsjT());
        long diffTime = 60 * 1000 * 15;
        // 相差十五分钟
        if (timeDifferenceInMillis <= diffTime) {
            return true;
        }
        return false;
    }

    private static void handlerScheduling(List<DriverScheduling> list, ReportViewResponseVo vo) {
        int planSignInCount = 0;
        int actualSignInCount = 0;
        int planSignOutCount = 0;
        int actualSignOutCount = 0;
        BeanUtils.copyProperties(list.get(0), vo);
        String exString = NO_EX;
        for (DriverScheduling scheduling : list) {
            // 获取计划签到|签退次数
            if (scheduling.getBcType().equals(BC_TYPE_OUT)) {
                planSignInCount = planSignInCount + 1;
                if (!Objects.isNull(scheduling.getSignInId())) {
                    actualSignInCount = actualSignInCount + 1;
                }

            } else {
                planSignOutCount = planSignOutCount + 1;
                if (!Objects.isNull(scheduling.getSignInId())) {
                    actualSignOutCount = actualSignOutCount + 1;
                }
            }

            // 判断是否异常
            if (!SIGN_NO_EX_NUM.equals(scheduling.getExType())) {
                exString = HAVE_EX;
            }
        }
        vo.setExString(exString);
        vo.setPlanSignInCount(planSignInCount);
        vo.setPlanSignOutCount(planSignOutCount);
        vo.setActualSignInCount(actualSignInCount);
        vo.setActualSignOutCount(actualSignOutCount);
    }

    public List<DriverScheduling> queryToDay(String date) {
        return schedulingMapper.queryToDay(date, null, null, null);
    }


}