package com.ruoyi.system.task;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.ServiceConstants;
import com.ruoyi.common.core.domain.entity.AsUser;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.CommonUtil;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.map.GeoUtils;
import com.ruoyi.common.utils.map.GpsCoordinateUtils;
import com.ruoyi.common.utils.onenet.LogEntry;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.system.domain.*;
import com.ruoyi.system.mapper.*;
import com.ruoyi.system.service.*;
import com.wechat.pay.java.service.refund.model.Refund;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

/**
 * 定时任务调度测试
 *
 * @author ruoyi
 */
@Slf4j
@Component("etTask")
public class EtTask {

    @Resource
    private EtOrderMapper etOrderMapper;

    @Autowired
    private IEtOperatingAreaService etOperatingAreaService;

    @Resource
    private SysUserMapper userMapper;

    @Autowired
    private IEtDividendDetailService dividendDetailService;

    @Autowired
    private ScheduledExecutorService scheduledExecutorService;

    @Autowired
    private IEtOrderService etOrderService;

    @Resource
    private AsDeviceMapper asDeviceMapper;

    @Autowired
    private IWxPayService wxPayService;

    @Autowired
    private IEtFeeRuleService etFeeRuleService;

    @Autowired
    private IEtRefundService etRefundService;

    @Autowired
    private RedisCache redisCache;

    @Resource
    private AsUserMapper asUserMapper;

    @Resource
    private EtLocationLogMapper etLocationLogMapper;

    @Autowired
    private IAsDeviceService deviceService;


    @Autowired
    private IEtModelService etModelService;

    @Resource
    private EtCouponClaimLogMapper etCouponClaimLogMapper;


    /**
     * 1.启动时判断是否有未取消预约的订单
     * 2.判断已完成的订单未退还押金的
     * 3.启动时判断是否分账
     */
    @Transactional
    @PostConstruct
    public void init() {
        log.info("=========================启动业务处理=========================");
        log.info("=========================开始=========================");
        /** 1.启动时判断是否有未取消预约的订单*/
//        uncancelledAppointmentHandle();

        /** 2.判断已完成的订单未退还押金的(根据et_refund表中的refund_result结果判断是否已经退款) */
        /** ①找出所有已完成的订单 status=4  type = 1  r.refund_result  IS NULL
         *  ②根据用户查询最后一次押金充值记录
         */
        List<EtOrder> orders = etOrderMapper.selectUserListFinishOrder();
        log.info("已完成的订单未退还押金的的订单 = " + JSON.toJSONString(orders));
        for(EtOrder order:orders){
//            EtFeeRule rule = etFeeRuleService.selectEtFeeRuleByRuleIdIncludeDelete(order.getRuleId());
//            if(ObjectUtil.isNull(rule)){
//                throw new ServiceException("骑行订单:【"+order.getOrderNo()+"】未找到该套餐【"+order.getRuleId()+"】");
//            }
            EtOperatingArea area = etOperatingAreaService.selectEtOperatingAreaByAreaId(order.getAreaId());
            AsUser asUser = asUserMapper.selectUserById(order.getUserId());
            Integer autoRefundDeposit = order.getAutoRefundDeposit();

            // 根据用户查询最后一次押金充值订单
            EtOrder etOrder = new EtOrder();
            etOrder.setUserId(order.getUserId());
            etOrder.setPaid(ServiceConstants.ORDER_PAY_STATUS_PAID);
            etOrder.setType(ServiceConstants.ORDER_TYPE_DEPOSIT);
            etOrder.setStatus(ServiceConstants.ORDER_STATUS_ORDER_END);
            List<EtOrder> etOrders = etOrderMapper.selectEtOrderList(etOrder);
            if (etOrders.size() > 0 || ObjectUtil.isNotNull(etOrders) ) {
                Optional<EtOrder> latestOrderOptional = etOrders.stream()
                        .max(Comparator.comparing(EtOrder::getPayTime));
                if (latestOrderOptional.isPresent()) {
                    EtOrder lastOrder = latestOrderOptional.get();
                    log.info("【系统启动】用户【{}】最后一次押金充值订单 : " + JSON.toJSONString(lastOrder),asUser.getUserId());
                    if(lastOrder.getTotalFee().compareTo(new BigDecimal(area.getDeposit()))!=0){
                        log.info("【系统启动】最后一次押金充值记录 金额与押金不一致,订单押金:【{}】,区域押金:【{}】",lastOrder.getTotalFee(),area.getDeposit());
                    }else{
                        // 根据最新的订单号,查询是否有退款记录
                        List<EtRefund> etRefunds = etRefundService.selectEtRefundByOrderNo(lastOrder.getOrderNo());
                        if(etRefunds.size() == 1){
                            EtRefund etRefund = etRefunds.get(0);
                            // 没有退款记录,发起退款
                            if(ObjectUtil.isNull(etRefund)){
                                // 根据订单支付时间 autoRefundDeposit个小时后退押金
                                String reason = autoRefundDeposit + "个小时后自动退押金";
                                Date payTime = order.getPayTime();
                                Date refundDepositTime = DateUtils.getTimeAfterXHours(payTime, autoRefundDeposit);
                                Date nowDate = DateUtils.getNowDate();
                                if (nowDate.after(refundDepositTime)) {
                                    log.info("【系统启动】用户【{}】押金充值订单【{}】已过期,开始自动退押金",asUser.getUserId(),lastOrder.getOrderNo());
                                    refundDeposit(asUser, lastOrder, reason);
                                }else{
                                    int timeDifferenceInMinutes = DateUtils.timeDifferenceInMinutes(payTime, nowDate);
                                    int i = autoRefundDeposit * 60;
                                    int delay = i - timeDifferenceInMinutes;
                                    log.info("【系统启动】用户【{}】押金充值订单【{}】未过期,【{}】分钟后退押金",asUser.getUserId(),lastOrder.getOrderNo(),delay);
                                    scheduledExecutorService.schedule(() -> {
                                        refundDeposit(asUser, lastOrder, reason);
                                    }, delay, TimeUnit.MINUTES);
                                }
                            }else{
                                // 有退款记录,判断是否成功
                                if(!Constants.SUCCESS2.equals(etRefund.getRefundResult())){
                                    log.info("【系统启动】押金退款未成功回调,退款单号:【{}】",etRefund.getRefundNo());
                                    // 根据退款单号查询退款信息
                                    Refund refund = wxPayService.queryByOutRefundNo(area.getAreaId(),etRefund.getRefundNo());
                                    if(ObjectUtil.isNotNull(refund) && Constants.SUCCESS2.equals(refund.getStatus().name())){
                                        // 更新退款记录
                                        etRefund.setRefundResult(Constants.SUCCESS2);
                                        etRefund.setUpdateTime(new Date());
                                        etRefundService.updateEtRefund(etRefund);
                                        log.info("【系统启动】更新押金退款回调成功,退款单号:【{}】",refund.getOutRefundNo());
                                    }
                                }
                            }
                        }

                    }
                }
            }
        }

        /** 3.启动时判断是否分账(根据订单号查询分账明细表是否有记录来判断是否分账) */
        /** ①找出所有已完成的骑行订单 status=4  type = 1  r.refund_result  IS NULL
         *  ②根据订单号查询分账明细表是否有记录
         *      有记录则已经分账过
         *      没值代表还未分账
         *          判断是否已过分账时间
         *              未过,计算出多少小时后分账
         *              已过,直接分账(记录分账明细表)
         */
        // 查询所有待分账的订单
//        List<EtOrder> needDividendOrders = etOrderMapper.selectNeedDividendOrder();
//        for(EtOrder order: needDividendOrders){
//            log.info("【系统启动】待分账订单:【{}】",order.getOrderNo());
//            EtOperatingArea area = etOperatingAreaService.selectEtOperatingAreaByAreaId(order.getAreaId());
//            if(dividendDetailService.isDividendComputedByOrderNo(order.getOrderNo())){
//                log.info("订单【{}】已经分账",order.getOrderNo());
//                break;
//            }
//            log.info("【系统启动】骑行订单【{}】未分账,开始分账",order.getOrderNo());
//            Date payTime = order.getPayTime();
//            Date dividendTime = DateUtils.getTimeAfterXHours(payTime, 24);//分账时间
//            Date nowDate = DateUtils.getNowDate();
//            if (nowDate.after(dividendTime)) {
//                log.info("【系统启动】骑行订单【{}】已过分账时间,开始分账",order.getOrderNo());
//                // 请求分账处理
//                Transaction transaction = wxPayService.queryOrderByOutTradeNo(order.getOrderNo());
////                if (callbackService.dividendHandle(transaction.getTransactionId(), order, area)) break;
//            }else{
//                int timeDifferenceInHours = DateUtils.timeDifferenceInHours(payTime, nowDate);
//                int delay = 24 - timeDifferenceInHours;
//                log.info("【系统启动】骑行订单【{}】未过分账时间,【{}】小时后开始分账",order.getOrderNo(),delay);
//                // 24小时后发起分账
////                scheduledExecutorService.schedule(() -> {
////                    // 请求分账处理
////                    Transaction transaction = wxPayService.queryOrderByOutTradeNo(order.getOrderNo());
////                    if (callbackService.dividendHandle(transaction.getTransactionId(), order, area)) return;
////                }, delay , TimeUnit.HOURS);
//            }
//        }
//        log.info("=========================结束=========================");
    }


    private void refundDeposit(AsUser asUser, EtOrder lastOrder, String reason) {
        String outRefundNo = IdUtils.getOrderNo("ref");
        lastOrder.setReason(reason);
        EtRefund refund1= etOrderService.createRefund(lastOrder, lastOrder.getTotalFee(), null, null, null, null, outRefundNo,ServiceConstants.REFUND_TYPE_DEPOSIT);
        if(etRefundService.insertEtRefund(refund1)>0){
            log.info("【自动退款】保存退款对象成功");
            // 新增资金流水记录
//            callbackService.capitalFlowRecords(lastOrder,ServiceConstants.FLOW_TYPE_DISBURSE,ServiceConstants.ORDER_TYPE_DEPOSIT_REFUND);
            // 更新用户信息,清除缓存
            asUser.setBalance(BigDecimal.ZERO);
            int updateUser = asUserMapper.updateUser(asUser);
            if(updateUser>0){
//                Collection<String> keys = SpringUtils.getBean(RedisCache.class).keys(CacheConstants.APP_LOGIN_TOKEN_KEY + "*");
//                redisCache.deleteObject(keys);
                log.info("【系统启动】退还押金,更新用户余额成功!");
            }
            Refund refund = wxPayService.refund(lastOrder, reason, lastOrder.getTotalFee(),outRefundNo);
            log.info("=================【系统启动】退还押金定时任务结束!!!==================");
        }else{
            throw new ServiceException("【系统启动】保存退款对象失败");
        }
    }

    private void uncancelledAppointmentHandle() {
        List<EtOrder> orders= etOrderMapper.selectAppointmentUnfinished();
        log.info("预约未完成的订单 = " + JSON.toJSONString(orders));
        for (EtOrder order:orders) {
            EtOperatingArea area = etOperatingAreaService.selectEtOperatingAreaByAreaId(order.getAreaId());
            AsDevice asDevice = asDeviceMapper.selectAsDeviceBySn(order.getSn());
            Date appointmentEndTime = DateUtils.getTimeAfterXMinutes(order.getAppointmentStartTime(), area.getTimeoutMinutes());//预约结束时间
            int timeDifferenceInSeconds = DateUtils.timeDifferenceInSeconds(appointmentEndTime, order.getAppointmentStartTime());//(超时时间-开始时间)的秒数
            int differenceInSeconds = DateUtils.timeDifferenceInSeconds(new Date(), order.getAppointmentStartTime());//(当前时间-开始时间)的秒数
            int delay = timeDifferenceInSeconds - differenceInSeconds;
            log.info("【定时取消预约】延迟:【{}】秒", delay);
            //定时取消预约
            scheduledExecutorService.schedule(() -> {
                log.error("【车辆超时预约】系统自动取消");
                EtOrder order1 = etOrderService.selectEtOrderByOrderNo(order.getOrderNo());
                log.info("【定时取消预约】重新获取订单信息:{}",JSON.toJSON(order1));
                if(order1.getPaid().equals(ServiceConstants.ORDER_PAY_STATUS_PAID)){//已支付订单,跳过
                    log.error("【车辆超时预约】订单已支付,跳过");
                    return;
                }
                log.error("【车辆超时预约】订单未支付,系统自动处理");
                //未支付   订单更新最后预约时间,并结束订单,做超出预约时间标记
                order.setStatus(ServiceConstants.ORDER_STATUS_CANCEL_APPOINTMENT);
                order.setAppointmentEndTime(new Date());
                order.setAppointmentTimeout("1");
                //计算预约费
                BigDecimal appointmentServiceFee = area.getAppointmentServiceFee();
                BigDecimal fee = appointmentServiceFee.multiply(new BigDecimal(area.getTimeoutMinutes()).divide(new BigDecimal(10)));
                order.setAppointmentFee(fee);
                order.setTotalFee(fee);
                order.setPayFee(fee);
                int update = etOrderService.updateEtOrder(order);
                if(update==0){
                    throw new ServiceException("【车辆超时预约】:更新订单状态失败");
                }
                // 改变车辆状态
                asDevice.setStatus(ServiceConstants.VEHICLE_STATUS_NORMAL);
                asDevice.setLockStatus(ServiceConstants.LOCK_STATUS_OPEN);
                int device = asDeviceMapper.updateAsDevice(asDevice);
                if(device==0){
                    log.error("【车辆超时预约】更新车辆状态失败");
                    throw new ServiceException("【车辆超时预约】更新车辆状态失败");
                }
            }, delay, TimeUnit.SECONDS);
        }
    }
    /**
     *  每天凌晨0点5分执行,计算分账结果
     *  cron: 0 5 0 * * ?
     */
    public void computeDividend()
    {
        log.info("每天凌晨0点5分执行,计算分账结果");
        // 获取昨天的订单,2024-05-26 00:00:00  -- 2024-05-26 23:59:59
        // 获取昨天日期格式: yyyy-MM-dd

        LocalDate yesterday = LocalDate.now().minusDays(1);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        String formattedYesterday = yesterday.format(formatter);
        log.info("获取昨天日期 = " + formattedYesterday);

        //判断该日期是否已经计算过分账结果
        if(dividendDetailService.isDividendComputed(LocalDate.now().format(formatter))){
            log.info("该日期已经计算过分账结果");
            return;
        }

        String startDateStr = formattedYesterday + " "+ Constants.DATE_FORMAT_START_PEREND;
        String endDateStr = formattedYesterday + " " +Constants.DATE_FORMAT_END_PEREND;

        EtOrder order = new EtOrder();
        order.setStartTime(startDateStr);
        order.setEndTime(endDateStr);
        order.setPaid(ServiceConstants.ORDER_PAY_STATUS_PAID);
        order.setStatus(ServiceConstants.ORDER_STATUS_ORDER_END);
        order.setType(ServiceConstants.ORDER_TYPE_RIDING);
        List<EtOrder> orderListByDate = etOrderMapper.selectEtOrderList(order);
        for(EtOrder order1:orderListByDate){
            EtDividendDetail etDividendDetail = new EtDividendDetail();
            EtOperatingArea area = etOperatingAreaService.selectEtOperatingAreaByAreaId(order1.getAreaId());

            SysUser sysUser = new SysUser();
            sysUser.setUserType("03");
            sysUser.setAreaId(area.getAreaId());
            List<SysUser> sysUsers = userMapper.selectUserList(sysUser);
            for(SysUser user : sysUsers){
                etDividendDetail.setAreaId(area.getAreaId());
                etDividendDetail.setPartnerId(user.getUserId());
                etDividendDetail.setOrderNo(order1.getOrderNo());
                etDividendDetail.setTotalAmount(order1.getTotalFee());
                etDividendDetail.setCreateTime(DateUtils.getNowDate());
                etDividendDetail.setDividendProportion(user.getDividendProportion());
                String dividendItem = user.getDividendItem();
                // todo 分账金额是骑行费,还是调度费,看分账项目  分账项目:1-骑行费(骑行费+预约费);2-调度费(调度费+管理费)
                BigDecimal dividendAmount = BigDecimal.ZERO;
                if(dividendItem.contains("1")){
                    dividendAmount.add(order1.getRidingFee().add(order1.getAppointmentFee()));//1-骑行费(骑行费+预约费)
                }else if(dividendItem.contains("2")){
                    dividendAmount.add(order1.getManageFee().add(order1.getManageFee()));//2-调度费(调度费+停车点外调度费)
                }
                BigDecimal divide = new BigDecimal(user.getDividendProportion()).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP);
                etDividendDetail.setDividendAmount(dividendAmount.multiply(divide));
                etDividendDetail.setDividendItem(dividendItem);
                log.info("保存分账明细 === " + JSON.toJSONString(etDividendDetail));
                int i = dividendDetailService.insertEtDividendDetail(etDividendDetail);
                if(i==0){
                    throw new ServiceException("保存分账明细失败");
                }
            }
            int totalDividendProportion = IntStream.of(sysUsers.stream()
                            .mapToInt(SysUser::getDividendProportion)
                            .toArray())
                    .sum();
            //算运营商自己的分账
            etDividendDetail.setAreaId(area.getAreaId());
            etDividendDetail.setPartnerId(0L);
            etDividendDetail.setOrderNo(order1.getOrderNo());
            etDividendDetail.setTotalAmount(order1.getTotalFee());
            etDividendDetail.setCreateTime(DateUtils.getNowDate());
            etDividendDetail.setDividendAmount(order1.getTotalFee().multiply(new BigDecimal(100-totalDividendProportion).divide(new BigDecimal(100),2, BigDecimal.ROUND_HALF_UP)));
            etDividendDetail.setDividendProportion(100-totalDividendProportion);
            etDividendDetail.setDividendItem("运营商");
            int i = dividendDetailService.insertEtDividendDetail(etDividendDetail);
            if(i==0){
                throw new ServiceException("保存分账明细失败");
            }
        }
    }

    /**
     *  开始骑行未结束的订单,1分钟算一次距离
     *  cron: 0 5 0 * * ?
     */
    public void computeDistance(){
        log.info("-------------------【定时任务】计算订单距离开始-------------------");
        EtOrder order = new EtOrder();
        order.setType("1");
        order.setStatus(ServiceConstants.ORDER_STATUS_RIDING);
        List<EtOrder> orders = etOrderService.selectEtOrderList(order);
        for(EtOrder etOrder:orders){
            String endTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.getNowDate());
            String startTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, etOrder.getUnlockTime());
            String tripRouteStr = deviceService.trajectory(etOrder.getSn(), startTime, endTime);
            if(StrUtil.isNotBlank(tripRouteStr)){
                double[][] doubles = GeoUtils.parseJsonTrack(tripRouteStr);
                double v = GeoUtils.calculateTotalDistance(doubles);
                etOrder.setDistance((int)Math.round(v));
                int updateEtOrder = etOrderService.updateEtOrder(etOrder);
                if(updateEtOrder>0){
                    log.info("【定时任务】计算订单距离成功:【orderNo="+etOrder.getOrderNo()+"】");
                }
            }
        }
    }

    /**
     *  一个星期删除一次onenet心跳日志
     *  cron: 0 5 0 * * ?
     *
     *  DELETE FROM et_location_log
     * WHERE create_time < NOW() - INTERVAL 7 DAY;
     */
    public void deleteLocationLog(){
        log.info("-------------------【定时任务】删除onenet心跳日志-------------------");
        etLocationLogMapper.deleteLocationLogByCreateTime();
    }


    /**
     *  车辆与订单状态同步
     *  1. 如果有正在骑行中的订单,车辆的状态是待骑行的,改成临时锁车,不发命令
     *  2. 如果车辆状态是骑行中或临时锁车,查询订单没有订单,则修改车辆状态为待骑行
     *
     */
    public void stausSynchronization(){
        log.info("-------------------【定时任务】车辆与订单状态同步-------------------");
        // 当前有骑行中的订单
        List<EtOrder> orders = etOrderService.getCurrentOrderList();
        for (EtOrder order:orders) {
            AsDevice device = asDeviceMapper.selectAsDeviceBySn(order.getSn());
            if(ObjectUtil.isNotNull(device) && device.getStatus().equals(ServiceConstants.VEHICLE_STATUS_NORMAL)){
                AsDevice device1 = new AsDevice();
                device1.setSn(device.getSn());
                device1.setStatus(ServiceConstants.VEHICLE_STATUS_TEMPORARILY_LOCK);
                device1.setLockStatus(ServiceConstants.LOCK_STATUS_CLOSE);
                int i = asDeviceMapper.updateAsDeviceBySn(device1);
                if(i>0){
                    log.info("【定时任务】车辆状态修改为临时锁车:【sn="+device.getSn()+"】");
                }
            }
        }

        // 2. 如果车辆状态是骑行中或临时锁车,查询当前没有订单,则修改车辆状态为待骑行
        QueryWrapper<AsDevice> wrapper = new QueryWrapper<>();
        wrapper.in("status", "3","4"); // 设备状态正常
        // 查询所有设备
        List<AsDevice> allDevices = asDeviceMapper.selectList(wrapper);
        for(AsDevice device:allDevices){
            if(ObjectUtil.isNotNull(etOrderService.getCurrentOrder2(device.getSn()))){
                continue;
            }else{
                AsDevice device1 = new AsDevice();
                device1.setSn(device.getSn());
                device1.setStatus(ServiceConstants.VEHICLE_STATUS_NORMAL);
                device1.setLockStatus(ServiceConstants.LOCK_STATUS_CLOSE);
                int i = asDeviceMapper.updateAsDeviceBySn(device1);
               if(i>0){
                   log.info("【定时任务】车辆状态修改为待骑行:【sn="+device.getSn()+"】");
               }
            }
        }
    }

    // 写一个定时,如果车辆是骑行中,没有现在骑行中的订单,则关闭车辆
    /**
     *  自动押金抵扣
     *     写一个定时任务处理所有的 7天前待支付的订单用押金抵扣,如果已经退押金的直接改成结束订单
     *      1. 查询所有待支付的订单,根据还车时间7天前的订单
     *      2. 如果订单金额是0,直接结束订单,修改订单状态为已支付
     *      3. 查询用户是否还有未退款的押金,如果有,则进行押金抵扣,如果没有,则结束订单
     */
    public void autoDeduction(){
        log.info("-------------------【定时任务】自动押金抵扣-------------------");
        /** 1. 查询所有待支付的订单,根据还车时间7天前的订单 */
        List<EtOrder> orders = etOrderMapper.selectToBePaidEtOrderList();
        if(ObjectUtil.isNotNull(orders) && orders.size()>0){
            for(EtOrder order:orders){
                if(order.getTotalFee().compareTo(BigDecimal.ZERO) == 0){
                    // 结束订单,修改订单状态为已支付
                    updateOrderPaid(order);
                }else{
                    etOrderService.deduction(order);
                }
            }
        }
    }

    /**
     *  押金抵扣不成功的修复
     *  1. 找出所有押金抵扣不成功的订单
     *  2. 将状态改成已结束
     *
     */
    public void deductionErrorOrder(){
        log.info("-------------------【定时任务】押金抵扣不成功的修复---开始----------------");
        List<EtOrder> orders = etOrderMapper.deductionErrorOrderList();
        for (EtOrder order:orders) {
            EtOrder order1 = new EtOrder();
            order1.setOrderId(order.getOrderId());
            order1.setStatus(ServiceConstants.ORDER_STATUS_ORDER_END);
            order1.setMark("押金抵扣修复,订单已结束");
            etOrderMapper.updateEtOrder(order1);
        }
        log.info("-------------------【定时任务】押金抵扣不成功的修复---结束----------------");
    }

    /** 更新订单为已支付*/
    private void updateOrderPaid(EtOrder order) {
        order.setPaid(ServiceConstants.ORDER_PAY_STATUS_PAID);
        order.setPayTime(DateUtils.getNowDate());
        order.setStatus(ServiceConstants.ORDER_STATUS_ORDER_END);
        order.setPayType(ServiceConstants.PAY_TYPE_YJ);
        order.setMark("超过7天系统自动押金抵扣");
        order.setDepositDeduction(ServiceConstants.IS_DEPOSIT_DEDUCTION);
        int updateEtOrder = etOrderMapper.updateEtOrder(order);
        if(updateEtOrder == 0){
            throw new ServiceException("押金抵扣失败,更新骑行订单失败");
        }
    }

    /** 如果还有未退款的押金,如果有,则进行押金抵扣 */
    private void autoDeductionHandle(EtOrder order){
        //  select * from   et_order o
        //        where o.status ='4' and o.paid = '1' and o.type = 1 and o.is_test = '0'
        //        GROUP BY o.user_id


    }

    /**
     *  更新设备的定位和电压 10秒一次
     *  cron: 0 10 0 * * ?
     */
    public void updateLocation10(){
        log.info("-------------------【定时任务10秒一次】更新设备的定位和电压-----开始--------------");
        // 记录开始时间
        long startTime = System.nanoTime();
        Collection<String> keys = redisCache.keys(CacheConstants.CACHE_DEVICE_KEY + "*");
        log.info("redis缓存中的数据:" + JSON.toJSONString(keys));
        for(String key:keys){
            String msg = redisCache.getCacheObject(key);
            log.info("redis缓存中的数据:" + msg);
            LogEntry logEntry = JSONObject.parseObject(msg, LogEntry.class);
            log.info("logEntry转换后的对象: logEntry---【{}】" , JSON.toJSONString(logEntry));
            LogEntry.LocationValue value = logEntry.getValue();
            AsDevice device = asDeviceMapper.selectAsDeviceByMac(logEntry.getDevName());
            if(!isRepeatMsg(msg,logEntry.getDevName())){
                log.info("device: sn={},【{}】",device.getSn() , JSON.toJSONString(device));
                if(ServiceConstants.LOCK_STATUS_OPEN.equals(device.getLockStatus()) && device.getStatus().equals(ServiceConstants.VEHICLE_STATUS_IN_USING)){
                    updateLocationHandle(msg, logEntry, value, device);
                }
            }
        }
        // 计算执行时间(以毫秒为单位)
        long duration = (System.nanoTime() - startTime) / 1_000_000;
        log.info("-------------------【定时任务10秒一次】更新设备的定位和电压----结束---------------"+duration+ " 毫秒");
    }

    private boolean isRepeatMsg(String msg,String mac){
        // 获取最后一条消息
        String lastMsg = etLocationLogMapper.getLastMsg(mac);
        if(ObjectUtil.isNotNull(lastMsg) && msg.equals(lastMsg)){
            return true;
        }else{
            return false;
        }
    }

    /**
     *  更新设备的定位和电压 5分钟一次
     *  cron: 0 20 0 * * ?
     */
    public void updateLocation300(){
        log.info("-------------------【定时任务5分钟一次】更新设备的定位和电压-----开始--------------");
        // 记录开始时间
        long startTime = System.nanoTime();
        Collection<String> keys = redisCache.keys(CacheConstants.CACHE_DEVICE_KEY + "*");
        log.info("redis缓存中的数据:" + JSON.toJSONString(keys));
        for(String key:keys){
            String msg = redisCache.getCacheObject(key);
            log.info("redis缓存中的数据:" + msg);
            LogEntry logEntry = JSONObject.parseObject(msg, LogEntry.class);
            log.info("logEntry转换后的对象: logEntry---【{}】" , JSON.toJSONString(logEntry));
            LogEntry.LocationValue value = logEntry.getValue();
            AsDevice device = asDeviceMapper.selectAsDeviceByMac(logEntry.getDevName());
            if(ServiceConstants.LOCK_STATUS_CLOSE.equals(device.getLockStatus())){
                updateLocationHandle(msg, logEntry, value, device);
            }
        }
        // 计算执行时间(以毫秒为单位)
        long duration = (System.nanoTime() - startTime) / 1_000_000;
        log.info("-------------------【定时任务5分钟一次】更新设备的定位和电压----结束---------------"+duration+ " 毫秒");
    }

    private void updateLocationHandle(String msg, LogEntry logEntry, LogEntry.LocationValue value, AsDevice device) {
        // 坐标转换  WGS84 转 GCJ02
        double[] doubles = coordinateConvert(value);
        BigDecimal lon = new BigDecimal(doubles[1]).setScale(8, RoundingMode.HALF_UP);
        BigDecimal lat = new BigDecimal(doubles[0]).setScale(8, RoundingMode.HALF_UP);

        asynchronousSaveLog(msg, logEntry.getAt(), logEntry.getDevName(), lon, lat, device);

        BigDecimal divide = new BigDecimal(value.getBat()).divide(new BigDecimal(10));
        device.setVoltage(divide.toString());//电压
        EtModel model = etModelService.selectEtModelByModelId(device.getModelId());
        device.setLastTime(DateUtils.getNowDate());
        device.setSignalStrength(value.getCsq());
        device.setQuality(value.getQ());
        if(ObjectUtil.isNotNull(model)){
            Integer remainingMileage = 0;
            if(StrUtil.isNotBlank(device.getVoltage())){
                remainingMileage = CommonUtil.getRemainingMileage(device.getVoltage(), model.getFullVoltage(), model.getLowVoltage(), model.getFullEndurance());
            }
            Integer electricQuantity = CommonUtil.getElectricQuantity(device.getVoltage(), model.getFullVoltage(), model.getLowVoltage());//电量百分百
            device.setRemainingMileage(remainingMileage);
            device.setRemainingPower(electricQuantity.toString());
        }
        if(BigDecimal.ZERO.compareTo(lon) != 0 && BigDecimal.ZERO.compareTo(lat) != 0){
            device.setLatitude(lat.toString());
            device.setLongitude(lon.toString());
            device.setLastLocationTime(DateUtils.getNowDate());
            device.setGps("1");
            // 信号强度
            device.setSatellites(value.getS());
        }else{
            device.setGps("0");
            device.setSatellites(0);
        }
        int i = deviceService.updateLocation(device);
        if(i>0){
            log.info("===============更新设备信息成功===========>" + logEntry.getDevName());
        }
    }

    /**
     * 异步保存定位
     */
    private void asynchronousSaveLog(String msg, long at,String mac,BigDecimal lon,BigDecimal lat,AsDevice device){
        //异步保存定位
        scheduledExecutorService.schedule(() -> {
            EtLocationLog etLocationLog = new EtLocationLog();
            etLocationLog.setOnenetMsg(msg);
            etLocationLog.setCreateTime(DateUtils.getNowDate());
            etLocationLog.setLongitude(lon.toString());
            etLocationLog.setLatitude(lat.toString());
            etLocationLog.setMac(mac);
            etLocationLog.setAt(new Date(at));
            etLocationLog.setStatus(device.getStatus());
            etLocationLog.setLockStatus(device.getLockStatus());
            etLocationLogMapper.insertEtLocationLog(etLocationLog);
        }, 0, TimeUnit.SECONDS);
    }

    /** 坐标转换 */
    @NotNull
    private double[] coordinateConvert(LogEntry.LocationValue value) {
        BigDecimal lon = new BigDecimal(value.getLon());
        BigDecimal lat = new BigDecimal(value.getLat());
//        log.info("WGS84经纬度(未计算):" + lon + "---" + lat);
        // 除以100
        lon = lon.divide(new BigDecimal(100), 10, RoundingMode.HALF_UP);
        lat = lat.divide(new BigDecimal(100), 10, RoundingMode.HALF_UP);
//        log.info("WGS84经纬度(除以100后):" + lon + "---" + lat);
        // 取出lon中后面的小数点
        String[] lonStr = getDecimalPart(lon);
        String[] latStr = getDecimalPart(lat);
//        log.info("WGS84经纬度(截取小数点):" + lonStr[0] + "---" + lonStr[1] + "---"+ latStr[0]+"---"+ latStr[1]);
        // 再将结果乘以5/3
        String lon2 = "0."+ lonStr[1];
        String lat2 = "0."+ latStr[1];
        BigDecimal lons = new BigDecimal(lon2).multiply(new BigDecimal(5).divide(new BigDecimal(3), 8, RoundingMode.HALF_UP));
        BigDecimal lats = new BigDecimal(lat2).multiply(new BigDecimal(5).divide(new BigDecimal(3), 8, RoundingMode.HALF_UP));
        BigDecimal lo = new BigDecimal(lonStr[0]).add(lons);
        BigDecimal la = new BigDecimal(latStr[0]).add(lats);
//        log.info("WGS84经纬度(计算后):" + lo + "---" + la);
        lo = lo.setScale(8, RoundingMode.HALF_UP);
        la = la.setScale(8, RoundingMode.HALF_UP);
//        log.info("WGS84经纬度(保留8为小数):" + lo + "---" + la);
        double[] doubles = GpsCoordinateUtils.calWGS84toGCJ02(la.doubleValue(), lo.doubleValue());
        return doubles;
    }

    private static String[] getDecimalPart(BigDecimal number) {
        // 将BigDecimal转换为字符串
        String numberStr = number.toPlainString();

        // 找到小数点的位置
        int indexOfDecimal = numberStr.indexOf(".");

        // 初始化结果数组
        String[] parts = new String[2];

        // 如果有小数点
        if (indexOfDecimal >= 0) {
            parts[0] = numberStr.substring(0, indexOfDecimal); // 整数部分
            parts[1] = numberStr.substring(indexOfDecimal + 1); // 小数部分
        } else {
            // 如果没有小数点,整数部分为整个字符串,小数部分为空
            parts[0] = numberStr;
            parts[1] = "";
        }

        return parts;
    }

    /**
     *  判断优惠券是否过期
     *
     */
    public void couponIsExpires(){
        log.info("-------------------【定时任务】判断优惠券是否过期---开始----------------");
        EtCouponUserLog etCouponUserLog = new EtCouponUserLog();
        etCouponUserLog.setStatus(ServiceConstants.COUPON_STATUS_UNUSED);
        List<EtCouponUserLog> couponUserLogs = etCouponClaimLogMapper.selectEtCouponClaimLogList(etCouponUserLog);
        // 获取当前时间
        Date currentTime = new Date();
        for(EtCouponUserLog couponLog :couponUserLogs){
            // 获取优惠券的过期时间
            Date expirationTime = couponLog.getExpirationTime();
            // 判断优惠券是否过期
            if (expirationTime != null && expirationTime.before(currentTime)) {
                // 如果过期,更新状态为已过期
                couponLog.setStatus(ServiceConstants.COUPON_STATUS_EXPIRED);
                // 更新到数据库
                int result = etCouponClaimLogMapper.updateEtCouponClaimLog(couponLog);
                if (result > 0) {
                    log.info("优惠券ID: {} 已过期,状态已更新为:已过期", couponLog.getCouponId());
                } else {
                    log.warn("优惠券ID: {} 更新状态失败", couponLog.getCouponId());
                }
            }
        }
        log.info("-------------------【定时任务】判断优惠券是否过期---结束----------------");
    }
}