package com.yida.data.device.studentCard.service.impl;

import cc.mrbird.febs.common.redis.service.RedisService;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yida.data.common.core.entity.card.EduStudentCard;
import com.yida.data.common.core.entity.card.EduStudentCardDisturbTime;
import com.yida.data.common.core.entity.card.EduStudentCardLocation;
import com.yida.data.common.core.entity.constant.CachePrefixConstant;
import com.yida.data.common.core.entity.system.Dept;
import com.yida.data.common.core.entity.user.EduStudent;
import com.yida.data.common.core.enums.CardDeviceEnum;
import com.yida.data.common.core.enums.DeviceStatusEnum;
import com.yida.data.common.core.exception.FebsException;
import com.yida.data.common.core.utils.Asserts;
import com.yida.data.common.core.utils.ExcelUtil;
import com.yida.data.common.core.vo.ImportPercentVO;
import com.yida.data.device.dto.studentCard.CardPageDTO;
import com.yida.data.device.dto.studentCard.CardWithAddressDTO;
import com.yida.data.device.dto.studentCard.DisturbStatusDTO;
import com.yida.data.device.studentCard.constant._810AMethod;
import com.yida.data.device.studentCard.mapper.EduStudentCardMapper;
import com.yida.data.device.studentCard.repository.EduStudentCardLocationRepository;
import com.yida.data.device.studentCard.service.EduStudentCardDisturbTimeService;
import com.yida.data.device.studentCard.service.EduStudentCardService;
import com.yida.data.device.studentCard.utils.JimiUtils;
import com.yida.data.device.studentCard.utils._810AUtil;
import com.yida.data.device.vo.studentCard.CardInfoVO;
import com.yida.data.device.vo.studentCard.CardPageVO;
import com.yida.data.device.vo.studentCard.CardWithAddressVO;
import com.yida.data.device.vo.studentCard.ListStudentCardVO;
import com.yida.data.device.vo.studentCard.LocationVO;
import com.yida.data.device.vo.OnlineCountVO;
import com.yida.data.device.vo.studentCard.TrackVO;
import com.yida.data.system.feign.RemoteDeptService;
import com.yida.data.user.dto.ListStudentDTO;
import com.yida.data.user.feign.RemoteStudentService;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;


/**
 * 电子学生证-设备管理 Service实现
 *
 * @author zhanghaijun
 * @date 2021-08-04 14:44:39
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class EduStudentCardServiceImpl extends ServiceImpl
    <EduStudentCardMapper, EduStudentCard> implements EduStudentCardService {

    private final EduStudentCardDisturbTimeService eduStudentCardDisturbTimeService;
    private final EduStudentCardLocationRepository eduStudentCardLocationRepository;

    private final JimiUtils jimiUtils;
    private final RedisService redisService;
    private final RemoteDeptService remoteDeptService;
    private final RemoteStudentService remoteStudentService;


    @Override
    public IPage<CardPageVO> listCardPage(CardPageDTO dto) {
        ListStudentDTO studentContion = ListStudentDTO.builder().schoolId(dto.getSchoolId()).campusId(dto.getCampusId())
            .sectionId(dto.getSectionId()).gradeId(dto.getGradeId()).classId(dto.getClassId()).stuName(dto.getName())
            .isQueryParent(false).isQuerySchoolName(false).isQueryUserDept(false)
            .build();
        List<EduStudent> studentList = remoteStudentService.listStudent(studentContion).getData();
        IPage<CardPageVO> res = baseMapper.listCardPage(dto.toPage(), dto,
            CollUtil.isNotEmpty(studentList) ? studentList.stream().map(EduStudent::getId).collect(Collectors.toList()) : null);
        if (CollUtil.isNotEmpty(res.getRecords())) {
            Map<Long, List<EduStudent>> studentGroup = studentList.stream().collect(Collectors.groupingBy(EduStudent::getId));
            for (CardPageVO record : res.getRecords()) {
                record.setName(studentGroup.get(record.getStudentId()).get(0).getStuName());
                Integer online = (Integer) redisService.get(CachePrefixConstant.DEVICE_CARD_ONLINE + record.getImei());
                record.setOnlineStatus(Optional.ofNullable(online).orElse(1));
            }
        }
        return res;
    }

    @Override
    public void exportCard(CardPageDTO dto, HttpServletResponse response) {
        if (CollUtil.isEmpty(dto.getDataList())) {
            ListStudentDTO studentContion = ListStudentDTO.builder().schoolId(dto.getSchoolId()).campusId(dto.getCampusId())
                .sectionId(dto.getSectionId()).gradeId(dto.getGradeId()).classId(dto.getClassId()).stuName(dto.getName())
                .isQueryUserDept(false).isQueryParent(false).isQuerySchoolName(false)
                .build();
            List<EduStudent> studentList = remoteStudentService.listStudent(studentContion).getData();
            List<CardPageVO> resList = baseMapper.listCard(dto,
                CollUtil.isNotEmpty(studentList) ? studentList.stream().map(EduStudent::getId).collect(Collectors.toList())
                    : null);
            if (CollUtil.isNotEmpty(resList)) {
                Map<Long, List<EduStudent>> studentGroup = studentList.stream().collect(Collectors.groupingBy(EduStudent::getId));
                for (CardPageVO res : resList) {
                    res.setName(studentGroup.get(res.getStudentId()).get(0).getStuName());
                    Integer online = (Integer) redisService.get(CachePrefixConstant.DEVICE_CARD_ONLINE + res.getImei());
                    res.setOnlineStatus(Optional.ofNullable(online).orElse(1));
                }
                dto.setDataList(resList);
            }
        }
        try {
            String fileName =
                URLEncoder.encode(LocalDateTimeUtil.format(LocalDate.now(), "yyyy-MM-dd") + "校园卡信息", "UTF-8")
                    .replaceAll("\\+", "%20");
            ExcelUtil.export(fileName, CardPageVO.class, dto.getDataList(), response);
        } catch (Exception e) {
            log.error("导出失败,msg:[{}]", e.getMessage());
            throw new FebsException("导出失败");
        }
    }

    @Override
    public CardInfoVO getInfo(Long cardId) {
        return baseMapper.getInfo(cardId);
    }

    @Override
    public void updateCardPhone(String imei, String oldPhone, String newPhone) {
        EduStudentCard card = getOne(Wrappers.<EduStudentCard>lambdaQuery().eq(EduStudentCard::getImei, imei));
        Asserts.isNotNull(card, "学生证不存在，请刷新后重试");
        Asserts.isTrue(oldPhone.equals(card.getStudentPhone()), "当前手机不正确，请检查后重试");
        card.setStudentPhone(newPhone);
        updateById(card);
    }

    @Override
    public List<CardWithAddressVO> listCardWithAddress(CardWithAddressDTO dto) {
        // 查询在线id
        Set<String> onlineImei = redisService.keys(CachePrefixConstant.DEVICE_CARD_ONLINE);
        if (CollUtil.isEmpty(onlineImei)) {
            onlineImei = CollUtil.newHashSet("000");
        } else {
            onlineImei = onlineImei.stream().map(x -> x.substring(CachePrefixConstant.DEVICE_CARD_ONLINE.length()))
                .collect(Collectors.toSet());
        }
        List<EduStudent> studentList = remoteStudentService.listStudent(
            ListStudentDTO.builder().schoolId(dto.getSchoolId()).campusId(dto.getCampusId()).sectionId(dto.getSectionId())
                .gradeId(dto.getGradeId()).classId(dto.getClassId()).isQueryParent(false).isQuerySchoolName(false)
                .isQueryUserDept(false).build())
            .getData();
        List<Long> studentIds = studentList.stream().map(EduStudent::getId).collect(Collectors.toList());
        dto.setStudentIds(studentIds);
        // 符合条件的学生证
        List<CardWithAddressVO> studentCardList = baseMapper.listStudentCard(dto, onlineImei);
        if (CollUtil.isNotEmpty(studentCardList)) {
            Map<Long, List<EduStudent>> studentGroup = studentList.stream()
                .collect(Collectors.groupingBy(EduStudent::getId));
            for (CardWithAddressVO studentCard : studentCardList) {
                if (studentGroup.containsKey(studentCard.getStudentId())) {
                    studentCard.setStudentName(studentGroup.get(studentCard.getStudentId()).get(0).getStuName());
                    studentCard.setOnlineStatus(onlineImei.contains(studentCard.getImei()) ? 0 : 1);
                }
            }
        }
        return studentCardList;
    }

    @Override
    public void flushCacheLocation(Long schoolId) {
        LambdaQueryWrapper<EduStudentCard> condition = Wrappers.lambdaQuery();
        if (schoolId != null) {
            condition.eq(EduStudentCard::getSchoolId, schoolId);
        }
        condition.select(EduStudentCard::getImei);
        List<String> imeiList = list(condition).stream().map(EduStudentCard::getImei).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(imeiList)) {
            List<EduStudentCard> saveCard = new ArrayList<>();
            List<String> withoutData = new ArrayList<>();
            for (String imei : imeiList) {
                EduStudentCard studentCard = new EduStudentCard();
                LocationVO location = (LocationVO) redisService.hget(CachePrefixConstant.DEVICE_CARD_LATEST_LOCATION, imei);
                if (location != null) {
                    studentCard.setOnlineStatus(redisService.hasKey(CachePrefixConstant.DEVICE_CARD_ONLINE + imei) ? 0 : 1);
                    studentCard.setImei(imei);
                    studentCard.setLng(location.getLng());
                    studentCard.setLat(location.getLat());
                    studentCard.setPosType(location.getType());
                    studentCard.setBattery(location.getBat());
                    saveCard.add(studentCard);
                } else {
                    withoutData.add(imei);
                }
            }
            // 更新定位数据
            baseMapper.updateLocaltionBatchByImei(saveCard);
            // 更新无数据的学生证为离线状态
            update(Wrappers.<EduStudentCard>lambdaUpdate().in(EduStudentCard::getImei, withoutData)
                .set(EduStudentCard::getOnlineStatus, 1));
        }
    }

    @Override
    public void importCard(String path, String key) {
        ImportPercentVO<String> vo = new ImportPercentVO<>();
        vo.setFinish(0);
        vo.setPercent(0);
        List<String> errMsg = new ArrayList<>();
        try {
            redisService.hset(CachePrefixConstant.STUDENT_CARD_IMPORT, key, vo);
            Workbook workbook = ExcelUtil.readExcel(new FileInputStream(path), "xlsx");
            Map<String, Dept> schoolCache = new HashMap<>();
            List<Map<Integer, Cell>> mapList = ExcelUtil.readData(workbook.getSheetAt(0), 1);
            List<EduStudentCard> cardList = new ArrayList<>();
            int rowNum = 0;
            int percent = 0;
            int size = mapList.size();

            for (Map<Integer, Cell> cellMap : mapList) {
                rowNum++;
                String schoolName = ExcelUtil.getCellValue(cellMap.get(0));
                String rfid = ExcelUtil.getCellValue(cellMap.get(1));
                String imei = ExcelUtil.getCellValue(cellMap.get(2));
                String stuName = ExcelUtil.getCellValue(cellMap.get(3));
                String mobile = ExcelUtil.getCellValue(cellMap.get(4));
                String stuNumber = ExcelUtil.getCellValue(cellMap.get(5));
                String mcType = ExcelUtil.getCellValue(cellMap.get(6));
                if (StrUtil.isBlank(schoolName)) {
                    break;
                }
                Dept school = schoolCache.containsKey(schoolName) ? schoolCache.get(schoolName)
                    : remoteDeptService.getSchoolByName(schoolName).getData();
                if (school == null) {
                    errMsg.add(String.format("第%d行:[%s]学校不存在", rowNum, schoolName));
                    continue;
                }
                if (StrUtil.isBlank(mcType)
                    || !mcType.contains(CardDeviceEnum.JIMI.getValue())
                    || !mcType.contains(CardDeviceEnum._810A.getValue())) {
                    errMsg.add(String.format("第%d行:[%s]设备类型不存在", rowNum, mcType));
                    continue;
                }
                EduStudent student = remoteStudentService.getStudentByStuNumAndSchool(stuNumber, school.getDeptId())
                    .getData();
                schoolCache.put(school.getDeptName(), school);
                if (student == null) {
                    errMsg.add(String.format("第%d行:[%s]学生不存在", rowNum, stuName));
                    continue;
                }
                EduStudentCard studentCard = new EduStudentCard();
                studentCard.setStudentId(student.getId());
                studentCard.setStudentPhone(mobile);
                studentCard.setImei(imei);
                studentCard.setRfid(rfid);
                studentCard.setBindStatus(1);
                studentCard.setOnlineStatus(1);
                studentCard.setSchoolId(school.getDeptId());
                studentCard.setMcType(mcType);
                cardList.add(studentCard);
                if (rowNum / size > percent) {
                    percent = rowNum / size;
                    vo.setPercent(rowNum / size);
                    redisService.hset(CachePrefixConstant.STUDENT_CARD_IMPORT, key, vo);
                }
            }
            saveBatch(cardList);
        } catch (Exception e) {
            log.error("导入失败", e);
        } finally {
            vo.setFinish(1);
            vo.setError(errMsg);
            redisService.hset(CachePrefixConstant.STUDENT_CARD_IMPORT, key, vo);
        }
    }

    @Override
    public void downloadTemplate(HttpServletResponse response) {
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("template/studentCard/card.xlsx");
        try {
            com.yida.data.common.core.utils.FileUtil.download(inputStream, "导入模板.xlsx", response);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
            throw new FebsException("下载失败");
        }
    }

    @Override
    public List<ListStudentCardVO> listStudentCard(Long parentId) {
        List<ListStudentCardVO> res = new ArrayList<>();
        List<EduStudent> studentList = remoteStudentService.listStudentByParent(parentId).getData();
        List<Long> studentIds = studentList.stream().map(EduStudent::getId).collect(Collectors.toList());
        List<EduStudentCard> cardList = list(
            Wrappers.<EduStudentCard>lambdaQuery().in(EduStudentCard::getStudentId, studentIds));
        // 今天的时间类型
        int weekType = Math.max(LocalDate.now().getDayOfWeek().getValue() - 5, 0);
        LocalTime now = LocalTime.now();
        for (EduStudent student : studentList) {
            for (EduStudentCard card : cardList) {
                if (card.getStudentId().equals(student.getId())) {
                    // 获取最新缓存定位信息
                    LocationVO latestLocation = (LocationVO) redisService
                        .hget(CachePrefixConstant.DEVICE_CARD_LATEST_LOCATION, card.getImei());
                    // 获取在线状态
                    Integer status = (Integer) redisService
                        .get(CachePrefixConstant.DEVICE_CARD_ONLINE + card.getImei());
                    // 获取免打扰状态
                    long disturbTimeCount = eduStudentCardDisturbTimeService.count(
                        Wrappers.<EduStudentCardDisturbTime>lambdaQuery()
                            .eq(EduStudentCardDisturbTime::getStudentId, card.getStudentId())
                            .like(EduStudentCardDisturbTime::getType, weekType)
                            .le(EduStudentCardDisturbTime::getStartTime, now)
                            .ge(EduStudentCardDisturbTime::getEndTime, now)
                    );
                    ListStudentCardVO cardVO = new ListStudentCardVO();
                    cardVO.setStudentId(student.getId());
                    cardVO.setAvatar(student.getAvatar());
                    cardVO.setStudentName(student.getStuName());
                    cardVO.setSchoolId(student.getSchoolId());
                    cardVO.setSchoolName(student.getSchoolName());
                    cardVO.setGradeName(student.getGradeName());
                    cardVO.setClassName(student.getClassName());
                    cardVO.setImei(card.getImei());
                    cardVO.setPhone(card.getStudentPhone());
                    cardVO.setLng(latestLocation != null ? latestLocation.getLng() : card.getLng());
                    cardVO.setLat(latestLocation != null ? latestLocation.getLat() : card.getLat());
                    cardVO.setBat(latestLocation != null ? latestLocation.getBat() : card.getBattery());
                    cardVO.setOnlineStatus(Optional.ofNullable(status).orElse(1));
                    cardVO.setMcType(card.getMcType());
                    cardVO.setDisturbStatus(disturbTimeCount > 0 ? 0 : 1);
                    res.add(cardVO);
                    break;
                }
            }
        }
        return res;
    }

    @Override
    public void setDisturbStatus(DisturbStatusDTO dto) {
        // 查询要设置的学生证
        List<EduStudentCard> list = list(Wrappers.lambdaQuery(new EduStudentCard())
            .in(EduStudentCard::getStudentId, dto.getStudentId()));
        List<EduStudentCardDisturbTime> times = dto.getTimes();
        List<EduStudentCardDisturbTime> saveTimeList = new ArrayList<>();
        for (EduStudentCard student : list) {
            for (EduStudentCardDisturbTime time : times) {
                EduStudentCardDisturbTime disturbTime = new EduStudentCardDisturbTime();
                disturbTime.setStartTime(time.getStartTime());
                disturbTime.setEndTime(time.getEndTime());
                disturbTime.setStudentId(student.getStudentId());
                disturbTime.setType(time.getType());
                saveTimeList.add(disturbTime);
            }
        }
        // 删除原有的免打扰时间后删除
        eduStudentCardDisturbTimeService.remove(Wrappers.lambdaQuery(new EduStudentCardDisturbTime())
            .in(EduStudentCardDisturbTime::getStudentId, dto.getStudentId()));
        eduStudentCardDisturbTimeService.saveBatch(saveTimeList);
        // 发送设备指令
        // 几米学生证
        List<String> time = new ArrayList<>();
        List<String> work = new ArrayList<>();
        List<String> sat = new ArrayList<>();
        List<String> sun = new ArrayList<>();
        StringBuilder _810a_condition = new StringBuilder();
        DateTimeFormatter hmFormat = DateTimeFormatter.ofPattern("HHmm");
        for (EduStudentCardDisturbTime disturbTime : times) {
            LocalTime startTime = disturbTime.getStartTime();
            LocalTime endTime = disturbTime.getEndTime();
            // 构造810A下发参数
            _810a_condition.append(startTime.format(hmFormat))
                .append(endTime.format(hmFormat))
                .append(disturbTime.getType().contains("0") ? "1121314151" : "1020304050")
                .append(disturbTime.getType().contains("1") ? "61" : "60")
                .append(disturbTime.getType().contains("2") ? "71" : "70")
                .append(",");
            String s;
            s = startTime.format(DateTimeFormatter.ofPattern("HH:mm-")) + endTime
                .format(DateTimeFormatter.ofPattern("HH:mm"));
            if (disturbTime.getType().contains("0")) {
                work.add(s);
            }
            if (disturbTime.getType().contains("1")) {
                sat.add(s);
            }
            if (disturbTime.getType().contains("2")) {
                sun.add(s);
            }
        }
        // 填充空元素构造指令
        int workSize = work.size();
        int satSize = sat.size();
        int sunSize = sun.size();
        for (int i = 0; i < 8 - workSize; i++) {
            work.add("");
        }
        for (int i = 0; i < 8 - satSize; i++) {
            sat.add("");
        }
        for (int i = 0; i < 8 - sunSize; i++) {
            sun.add("");
        }
        time.add("1");
        time.addAll(work);
        time.addAll(sat);
        time.addAll(sun);
        // 810A学生证
        String _810aStr =
            _810a_condition.length() > 0 ? _810a_condition.substring(0, _810a_condition.length() - 1) : "0000000010203040506070";
        // 根据设备类型下发命令
        for (EduStudentCard card : list) {
            if (card.getMcType().contains(CardDeviceEnum.JIMI.getValue())) {
                jimiUtils.setStealth(card.getImei(), time);
            } else if (card.getMcType().contains(CardDeviceEnum._810A.getValue())) {
                LinkedHashMap<String, String> params = new LinkedHashMap<>();
                params.put("DEVICENUM", card.getImei());
                params.put("SILENTGROUPS", _810aStr);
                _810AUtil.convertAndSend(_810AMethod.DEV_SILENT.getValue(), params);
            }
        }
    }

    @Override
    public List<TrackVO> listTrack(String imei, Long cardId, String startTime, String endTime) {
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime parse = LocalDateTime.parse(endTime, df);
        //如果结束时间比现在时间还大，则取现在时间
        if (now.isBefore(parse)) {
            endTime = df.format(now);
        }
        EduStudentCard card = getOne(Wrappers.<EduStudentCard>lambdaQuery()
            .eq(EduStudentCard::getImei, imei));
        List<TrackVO> list = new ArrayList<>();
        if (card.getMcType().contains(CardDeviceEnum.JIMI.getValue())) {
            JSONObject trackInformation = jimiUtils.getTrackInformation(imei, startTime, endTime);
            if (trackInformation.getInt("code") == 0) {
                JSONArray result = trackInformation.getJSONArray("result");
                if (CollUtil.isNotEmpty(result)) {
                    for (Object o : result) {
                        Map<String, Object> map = (Map<String, Object>) o;
                        TrackVO trackVO = new TrackVO();
                        trackVO.setGpsTime(map.get("gpsTime").toString());
                        trackVO.setLat((Double) map.get("lat"));
                        trackVO.setLng((Double) map.get("lng"));
                        list.add(trackVO);
                    }
                }
            }
        } else if (card.getMcType().contains(CardDeviceEnum._810A.getValue())) {
            List<EduStudentCardLocation> positionList = eduStudentCardLocationRepository
                .findByImeiAndDeviceTimeBetweenOrderByDeviceTime(imei,
                    LocalDateTime.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")).toInstant(
                        ZoneOffset.of("+8")
                    ).toEpochMilli(),
                    LocalDateTime.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
                        .toInstant(ZoneOffset.of("+8")).toEpochMilli());
            if (CollUtil.isNotEmpty(positionList)) {
                for (EduStudentCardLocation position : positionList) {
                    if (position.getLat() == null || position.getLng() == null) {
                        continue;
                    }
                    TrackVO track = new TrackVO();
                    track.setGpsTime(
                        position.getDeviceTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                    track.setLng(position.getLng());
                    track.setLat(position.getLat());
                    track.setPosType(position.getType());
                    list.add(track);
                }
            }
        }
        return list;
    }

    @Override
    public List<CardPageVO> listStudentCardByImeiOrName(Long schoolId, String keyword) {
        return baseMapper.listStudentCardByImeiOrName(schoolId, keyword);
    }

    /**
     * 定时获取定位信息
     */
    @Override
    public void updateCardPos() {
        JSONObject location = jimiUtils.getAllLocation();
        log.info("pull jimi location info");
        List<String> onlineList = new ArrayList<>();
        if (location.getInt("code") == 0) {
            JSONArray datas = location.getJSONArray("result");
            List<EduStudentCard> cardList = new ArrayList<>();
            for (Object data : datas) {
                JSONObject dataJson = (JSONObject) data;
                EduStudentCard studentCard = new EduStudentCard();
                studentCard.setImei(dataJson.getStr("imei"));
                studentCard.setPosType(EduStudentCard.POS_TYPE.get(dataJson.getStr("posType")));
                studentCard.setPosTime(LocalDateTimeUtil.parse(dataJson.getStr("gpsTime"), "yyyy-MM-dd " +
                    "HH:mm:ss"));
                studentCard.setLng(dataJson.getDouble("lng"));
                studentCard.setLat(dataJson.getDouble("lat"));
                studentCard.setBattery(dataJson.getInt("electQuantity"));
                cardList.add(studentCard);
                onlineList.add(studentCard.getImei());
            }
            baseMapper.updateLocaltionBatchByImei(cardList);
        }
        redisService.del(CachePrefixConstant.DEVICE_CARD_ONLINE);
        redisService.llSet(CachePrefixConstant.DEVICE_CARD_ONLINE, onlineList, 300L);
    }

    /**
     * 定时检查在线状态
     */
    @Override
    public void updateCardStatus() {
        List<Object> list = redisService.lGet(CachePrefixConstant.DEVICE_CARD_ONLINE, 0L, -1L);
        if (CollUtil.isEmpty(list)) {
            list.add("0");
        }
        baseMapper.updateOnline(list, 0);
        baseMapper.updateOnline(list, 1);
    }

    @Override
    public LocationVO getLocation(String imei) {
        EduStudentCard card = getOne(Wrappers.<EduStudentCard>lambdaQuery().eq(EduStudentCard::getImei, imei));
        Integer isOnline = (Integer) redisService.get(CachePrefixConstant.DEVICE_CARD_ONLINE + imei);
        // 获取最新缓存数据
        LocationVO location = (LocationVO) redisService.hget(CachePrefixConstant.DEVICE_CARD_LATEST_LOCATION, imei);
        if (location == null) {
            location = new LocationVO();
            location.setLng(card.getLng());
            location.setLat(card.getLat());
            location.setType(card.getPosType());
            location.setBat(card.getBattery());
            location.setDeviceTime(card.getPosTime());
        }
        location.setOnline(Optional.ofNullable(isOnline).orElse(1));
        return location;
    }

    @Override
    public void refreshLocation(String imei) {
        EduStudentCard card = getOne(Wrappers.<EduStudentCard>lambdaQuery().eq(EduStudentCard::getImei, imei));
        Integer isOnline = (Integer) redisService.get(CachePrefixConstant.DEVICE_CARD_ONLINE + imei);
        Asserts.isTrue(isOnline != null && isOnline == 0, "设备不在线");
        // 按种类刷新定位信息
        if (card.getMcType().contains(CardDeviceEnum.JIMI.getValue())) {
            jimiUtils.nowGps(imei);
            JSONObject locationJson = jimiUtils.getNowLocaltion(imei);
            JSONArray result = (JSONArray) locationJson.get("result");
            JSONObject location = result.getJSONObject(0);

            if ("0".equals(location.getStr("status"))) {
                update(Wrappers.lambdaUpdate(new EduStudentCard())
                    .eq(EduStudentCard::getImei, imei)
                    .set(EduStudentCard::getOnlineStatus, DeviceStatusEnum.OFFLINE.getValue()));
                redisService.del(CachePrefixConstant.DEVICE_CARD_ONLINE + imei);
                throw new FebsException("设备不在线");
            }
            // 缓存定位数据
            LocationVO locationVO = new LocationVO();
            locationVO.setLng(location.getDouble("lng"));
            locationVO.setLat(location.getDouble("lat"));
            locationVO.setDeviceTime(
                LocalDateTime.parse(location.getStr("gpsTime"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            redisService.hset(CachePrefixConstant.DEVICE_CARD_LATEST_LOCATION, imei, locationVO);
        } else if (card.getMcType().contains(CardDeviceEnum._810A.getValue())) {
            LinkedHashMap<String, String> param = new LinkedHashMap<>();
            param.put("DEVICENUM", imei);
            _810AUtil.convertAndSend(_810AMethod.CURR_POS.getValue(), param);
        }
    }

    @Override
    public void updateCard(EduStudentCard card) {
        if (card.getCardId() == null && StrUtil.isNotBlank(card.getImei())) {
            EduStudentCard existCard = getOne(Wrappers.<EduStudentCard>lambdaQuery().eq(EduStudentCard::getImei, card.getImei()));
            if (existCard != null) {
                card.setCardId(existCard.getCardId());
            }
        }
        Asserts.isNotNull(card.getCardId(), "请选择学生证");
        updateById(card);
    }

    @Override
    public void issueMonitor(String mobile, String imei) {
        EduStudentCard card = getOne(Wrappers.<EduStudentCard>lambdaQuery().eq(EduStudentCard::getImei, imei));
        Asserts.isNotNull(card, "学生证不存在");
        if (card.getMcType().contains(CardDeviceEnum._810A.getValue())) {
            LinkedHashMap<String, String> params = new LinkedHashMap<>();
            params.put("DEVICENUM", imei);
            params.put("MONITORPHONE", mobile);
            _810AUtil.convertAndSend(_810AMethod.DEV_MONITOR.getValue(), params);
        }
    }

    @Override
    public OnlineCountVO getCardDistrib(List<Long> schoolIds) {
        OnlineCountVO vo = new OnlineCountVO();
        if (CollUtil.isNotEmpty(schoolIds)) {
            List<EduStudentCard> cardList = list(Wrappers.<EduStudentCard>lambdaQuery().in(EduStudentCard::getSchoolId, schoolIds)
                .select(EduStudentCard::getOnlineStatus));
            Map<Integer, List<EduStudentCard>> onlineGroup = cardList.stream()
                .collect(Collectors.groupingBy(EduStudentCard::getOnlineStatus));
            vo.setOnlineNum(onlineGroup.getOrDefault(1, Collections.emptyList()).size());
            vo.setOfflineNum(onlineGroup.getOrDefault(0, Collections.emptyList()).size());
        }
        return vo;
    }
}

