SignInServiceImpl.java 13.2 KB
package com.ruoyi.in.service.impl;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Base64;
import java.util.Date;
import java.util.List;
import java.util.Objects;

import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.common.utils.uuid.Seq;
import com.ruoyi.common.utils.uuid.UUID;
import com.ruoyi.driver.domain.Driver;
import com.ruoyi.driver.mapper.DriverMapper;
import com.ruoyi.equipment.domain.Equipment;
import com.ruoyi.equipment.mapper.EquipmentMapper;
import com.ruoyi.pojo.response.ResponseScheduling;
import com.ruoyi.pojo.response.SignInResponseVo;
import com.ruoyi.service.ThreadJobService;
import com.ruoyi.utils.ConstDateUtil;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.in.mapper.SignInMapper;
import com.ruoyi.in.domain.SignIn;
import com.ruoyi.in.service.ISignInService;

import javax.annotation.Resource;

import static com.ruoyi.common.ConstDriverProperties.PERSONNEL_POSTS_DRIVER;
import static com.ruoyi.common.ErrorTypeProperties.*;
import static com.ruoyi.common.ConstSignInConstSignInProperties.*;
import static com.ruoyi.common.redispre.GlobalRedisPreName.DRIVER_SCHEDULING_PRE;
import static com.ruoyi.common.redispre.GlobalRedisPreName.REDIS_SIGN_IN_DRIVER_ALCOHOL_OVERFLOW;

/**
 * 签到Service业务层处理
 *
 * @author guzijian
 * @date 2023-07-05
 */
@Service
public class SignInServiceImpl implements ISignInService {

    private Logger log = LoggerFactory.getLogger(SignInServiceImpl.class);
    @Autowired
    private SignInMapper signInMapper;

    @Autowired
    private DriverMapper driverMapper;

    @Autowired
    private EquipmentMapper equipmentMapper;

    @Autowired
    private RedisCache redisCache;

    @Resource
    private ThreadJobService threadJobConfig;

    /**
     * 查询签到
     *
     * @param id 签到主键
     * @return 签到
     */
    @Override
    public SignIn selectSignInById(Long id) {
        return signInMapper.selectSignInById(id);
    }

    /**
     * 查询签到列表
     *
     * @param signIn 签到
     * @return 签到
     */
    @Override
    public List<SignIn> selectSignInList(SignIn signIn) {
        return signInMapper.selectSignInList(signIn);
    }

    /**
     * 新增签到
     *
     * @param signIn 签到
     * @return 结果
     */
    @Override
    public AjaxResult insertSignIn(SignIn signIn) {
        // 查询员工信息
        Driver driver = driverMapper.getDriverInfoByJobCode(signIn.getJobCode());
        if (Objects.isNull(driver)) return AjaxResult.error("这个工号的员工不存在!");
        // 查询地址
        Equipment equipment = equipmentMapper.selectEquipmentByDeviceId(signIn.getDeviceId());
        SignInResponseVo vo = getSignInResponseVo(signIn, equipment);
        signIn.setCreateTime(new Date());
        signIn.setRemark("");
        // 签到检查 酒精检查 超时检查 排班检查
        if (checkSignIn(signIn, driver)) {
            signIn.setStatus(SIGN_IN_SUCCESS);
        } else {
            signIn.setStatus(SIGN_IN_FAIL);
        }
        signIn.setIp(IpUtils.getIpAddr());
        // base64转图片
//        uploadImage(signIn);
        signInMapper.insertSignIn(signIn);
        // TODO redis 存储每个人的签到 用于后续考勤

        // 驾驶人员二次签到酒精测试异常
        if (PERSONNEL_POSTS_DRIVER.equals(driver.getPosts()) && SIGN_IN_FAIL.equals(signIn.getStatus()) && signIn.getRemark().contains(ALCOHOL_SIGN_IN_ERROR)) {
            AjaxResult result = getAjaxResultByDriverSignInfo(signIn, vo);
            if (!Objects.isNull(result)) {
                return result;
            }
        }

        return SIGN_IN_SUCCESS.equals(signIn.getStatus()) ? AjaxResult.success(SIGN_IN_SUCCESS_STRING, vo) : AjaxResult.error(SIGN_IN_ERROR + signIn.getRemark(), vo);
    }

    /**
     * 修改签到
     *
     * @param signIn 签到
     * @return 结果
     */
    @Override
    public int updateSignIn(SignIn signIn) {
        signIn.setUpdateTime(DateUtils.getNowDate());
        return signInMapper.updateSignIn(signIn);
    }

    /**
     * 批量删除签到
     *
     * @param ids 需要删除的签到主键
     * @return 结果
     */
    @Override
    public int deleteSignInByIds(Long[] ids) {
        return signInMapper.deleteSignInByIds(ids);
    }

    /**
     * 删除签到信息
     *
     * @param id 签到主键
     * @return 结果
     */
    @Override
    public int deleteSignInById(Long id) {
        return signInMapper.deleteSignInById(id);
    }

    @Override
    public AjaxResult addSignIn(SignIn signIn) throws IOException {
        // 查询员工信息
        Driver driver = driverMapper.getDriverInfoByJobCode(signIn.getJobCode());
        if (Objects.isNull(driver)) return AjaxResult.error("这个工号的员工不存在!");
        // 查询地址
        Equipment equipment = equipmentMapper.selectEquipmentByDeviceId(signIn.getDeviceId());
        SignInResponseVo vo = getSignInResponseVo(signIn, equipment);
        signIn.setCreateTime(new Date());
        signIn.setRemark("");
        // 签到检查
        if (checkSignIn(signIn, driver)) {
            signIn.setStatus(SIGN_IN_SUCCESS);
        } else {
            signIn.setStatus(SIGN_IN_FAIL);
        }
        // base64转图片
        signIn.setIp(IpUtils.getIpAddr());

        uploadImage(signIn);
        signInMapper.insertSignIn(signIn);
        // TODO redis 存储每个人的签到 用于后续考勤

        // 驾驶人员二次签到酒精测试异常
        if (PERSONNEL_POSTS_DRIVER.equals(driver.getPosts()) && SIGN_IN_FAIL.equals(signIn.getStatus()) && signIn.getRemark().contains(ALCOHOL_SIGN_IN_ERROR)) {
            AjaxResult result = getAjaxResultByDriverSignInfo(signIn, vo);
            if (!Objects.isNull(result)) {
                return result;
            }
        }
        return SIGN_IN_SUCCESS.equals(signIn.getStatus()) ? AjaxResult.success(SIGN_IN_SUCCESS_STRING, vo) : AjaxResult.error(SIGN_IN_ERROR + signIn.getRemark(), vo);
    }

    private SignInResponseVo getSignInResponseVo(SignIn signIn, Equipment equipment) {
        SignInResponseVo vo = new SignInResponseVo();
        if (Objects.isNull(equipment)) {
            equipment = new Equipment();
        }
        vo.setAddress(equipment.getAddress());
        vo.setDeviceId(signIn.getDeviceId());
        return vo;
    }

    private AjaxResult getAjaxResultByDriverSignInfo(SignIn signIn, SignInResponseVo vo) {
        String key = REDIS_SIGN_IN_DRIVER_ALCOHOL_OVERFLOW + ConstDateUtil.formatDate("yyyyMMdd");
        // 驾驶员酒精测试连续超标两次则提示换人
        Integer count = redisCache.getCacheMapValue(key, signIn.getJobCode());
        if (Objects.isNull(count) || count.equals(0)) {
            count = 1;
            redisCache.setCacheMapValue(key, signIn.getJobCode(), count);
        } else {
            count = redisCache.setIncrementMapValue(key, signIn.getJobCode(), 1);
        }
        if (SIGN_IN_ERROR_COUNT.compareTo(count) <= 0) {
            // TODO
            return AjaxResult.error(SIGN_IN_ERROR + signIn.getRemark() + ",酒精测试不通过" + count + "次请更换车辆驾驶员", vo);
        }
        return null;
    }

    private boolean checkSignIn(SignIn signIn, Driver driver) {
        boolean result = true;
        if (Objects.isNull(driver)) {
            return true;
        }
        // TODO 排班校验 和签到超时校验 非司售未作
        result = checkWorkDay(signIn, driver.getPosts());

        // 酒精测试校验 确定 且员工工种是驾驶员
        if (ALCOHOL_FLAG_YES.equals(signIn.getAlcoholFlag()) && PERSONNEL_POSTS_DRIVER.equals(driver.getPosts())) {
            if (new BigDecimal(20).compareTo(signIn.getAlcoholIntake()) > 0) {
                result = true;
            } else {
                signIn.setRemark(signIn.getRemark() + ALCOHOL_SIGN_IN_ERROR);
                result = false;
            }
        }
        return result;
    }


    /**
     * 排班和超时检查
     *
     * @param signIn
     * @param posts
     * @return
     */
    private boolean checkWorkDay(SignIn signIn, String posts) {
        boolean result = true;
        List<ResponseScheduling> jobs = null;
        switch (posts) {
            case "驾驶员":
                // 查询工号对应的排班 司售人员
                jobs = redisCache.getCacheMapValue(DRIVER_SCHEDULING_PRE + ConstDateUtil.formatDate("yyyyMMdd"), signIn.getJobCode());
                if (Objects.isNull(jobs) || jobs.size() == 0) {
                    signIn.setRemark(signIn.getRemark() + WORK_DAY_ERROR);
                    result = false;
                }
                break;
            case "1":
                // 查询工号对应的排班 非司售人员
                // 查询数据库
                result = true;
                break;
        }

        // 超时校验司售人员
        if (result) {
            // TODO 非司售人员
            result = checkTimeOut(signIn, jobs, posts);
        }
        return result;
    }

    private boolean checkTimeOut(SignIn signIn, List<ResponseScheduling> driver, String posts) {
        // 那发车时间和到站时间作为开始上班的时间
        if (PERSONNEL_POSTS_DRIVER.equals(posts)) {
            return driverCheckTimeOut(signIn, driver);
        } else {
            return true;
        }
    }

    private static boolean driverCheckTimeOut(SignIn signIn, List<ResponseScheduling> driver) {
        LocalDateTime startTime = ConstDateUtil.getLocalDateTimeByLongTime(driver.get(0).getFcsjT());
        LocalDateTime endTime = ConstDateUtil.getLocalDateTimeByLongTime(driver.get(driver.size() - 1).getZdsjT());
        LocalDateTime signTime = ConstDateUtil.getLocalDateTimeByLongTime(signIn.getCreateTime().getTime());
        long morningBetween = ChronoUnit.MINUTES.between(startTime, signTime);
        long afternoonBetween = ChronoUnit.MINUTES.between(endTime, signTime);
        // 签到范围一小时内
        if (Math.abs(morningBetween) <= 60 || Math.abs(afternoonBetween) <= 60) {
            return true;
        } else {
            signIn.setRemark(signIn.getRemark() + SIGN_IN_TIMEOUT);
            return false;
        }
    }

    private void uploadImage(SignIn signIn) throws IOException {
        String base64 = signIn.getImage();
        // 图片路径
        String filePath = RuoYiConfig.getUploadPath();
        // 固定jpg文件
        String fileName = "";
        // 看是否带有base64前缀 有就判断文件类型 没有默认jpg
        fileName = checkImageBase64Format(signIn.getImage());
        fileName = extractFilename(fileName);
        // 获取相对路径
        String absPath = getAbsoluteFile(filePath, fileName).getAbsolutePath();

        // 获取文件上传路径
        String pathFileName = getPathFileName(filePath, fileName);
        signIn.setImage(pathFileName);
        // 异步上传文件
        threadJobConfig.asyncStartUploadBase64Image(absPath, base64);
    }

    /**
     * 获取相对路径名
     *
     * @param uploadDir
     * @param fileName
     * @return
     * @throws IOException
     */
    public File getAbsoluteFile(String uploadDir, String fileName) throws IOException {
        File desc = new File(uploadDir + File.separator + fileName);

        if (!desc.exists()) {
            if (!desc.getParentFile().exists()) {
                desc.getParentFile().mkdirs();
            }
        }
        return desc;
    }

    /**
     * 获取扩展文件名
     *
     * @param extendFileName
     * @return
     */
    public String extractFilename(String extendFileName) {
        return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),
                FilenameUtils.getBaseName(UUID.randomUUID().toString().replace("-", "")), Seq.getId(Seq.uploadSeqType), extendFileName);
    }

    /**
     * @param uploadDir
     * @param fileName
     * @return
     * @throws IOException
     */
    public String getPathFileName(String uploadDir, String fileName) throws IOException {
        int dirLastIndex = RuoYiConfig.getProfile().length() + 1;
        String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
        return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
    }

    /**
     * 检查文件类型
     *
     * @param base64ImgData
     * @return
     */
    public static String checkImageBase64Format(String base64ImgData) {
        byte[] b = Base64.getDecoder().decode(base64ImgData);
        String type = "";
        if (0x424D == ((b[0] & 0xff) << 8 | (b[1] & 0xff))) {
            type = "bmp";
        } else if (0x8950 == ((b[0] & 0xff) << 8 | (b[1] & 0xff))) {
            type = "png";
        } else {
            type = "jpg";
        }
//        else if (0xFFD8 == ((b[0] & 0xff) << 8 | (b[1] & 0xff))) {
//            type = "jpg";
//        }
        return type;
    }
}