MybatisPlus处理业务数据新思路

news2024/11/24 11:43:50

文章目录

    • @[toc]
  • 1.问题
  • 2.新姿势
  • 3.总结

1.问题

  由于之前搞了一个停车场的项目,有一个区域的停车场收费被投诉了被举报没有按照政府要求来计费,所以需要将之前的那些停车计费的数据重新计算,将多缴费的钱拿去交罚款,这个就很坑,计费规则如下:

图片

  规则是公共停车场三类区的小型车,上图标准红色的那条计费规则,这个需求也是有点变态的,给我算了两天才把数据处理好了的,历史数据有1.7w多条,给的是一个execle表格的历史数据,所以需要搞一个表,将需要的列导入到表中,然后在一个表中设计了几个冗余的字段记录了:停车时长,夜间停车时长,夜间停车费用、白天停车时长、白天停车费用,多收费用、免费时长等字段,然后将数据写代码全部查出来,然后计算填充每一条这几个字段后使用mybatisPlus的Mapper的updateById或者使用updateBatchById更新这些数据都很慢很慢,然后我想了个办法,用两张表来搞,一张表存原始数据(导入的历史数据),一张表存计算后的结果数据。

  原始数据表截图:

图片

  数据处理的结果数据:

图片

  首先将原始数据数据量少的话,一次性读入内存,数据量大的话,分页多次读入内存计算处理,然后计算的数据结果使用mybatisPlus批量插入到数据处理的结果这张表中,采用mybatisPlus的批量插入sql注入器,这种方式是相当的快。

  这个需求的难点就是去计算白天停车时长、夜间停车时长还有根据油车或者是电车计算费用;

  油车7位车牌:停车时长小于0.25h不收费,超过0.25h才计费,大于等于0.25h小于等于0.5小时,按首小时每半小时1.5元计费,这种情况是1.5元,大于0.5小于等于1小时也是按首小时每半小时1.5元计费,这种情况下计算的都是3元。

  电车8位车牌:停时长小于2h内不收费,超过2小时才计费,入场时间加2小时后在计算两个时间段的费用。

  白天时长是不足半小时按半小时算(半小时周期计费),夜间时长是不足一小时按一小时算(整点小时周期计费)。

  处理白天或夜间停车时长:

private Double dayOrNightTimeCalculate(Boolean isType, Double d) {
        if (d <= 0) {
            return 0d;
        }
        log.info("dayOrNightTimeCalculate.isType:{},d:{}", isType, d);
        if (isType) {
            // 白天时长计算 (半小时周期计费),这段逻辑处理的时间,简单解释下:白天计算费用 = 1.5 * 2 + [(t -1) * 0.5 ]/0.5 这段逻辑就的返回直接加上3块就是总费用了 ,t是白天停车时长 
            double v = d / 0.5;
            int discuss = (int) v;
            double remain = d % 0.5;
            if (remain > 0) {
                log.info("半小时计费停车时长计算前;{}", d);
                d = (discuss + 1) * 0.5;
                log.info("半小时计费停车时长计算后;{}", d);
            }
        } else {
            // 夜间时长计算 (整点小时时周期计费)
            double v = d / 1.0;
            int discuss = (int) v;
            double remain = d % 1.0;
            if (remain > 0) {
                log.info("整点计费停车时长计算前;{}", d);
                d = (discuss + 1) * 1.0;
                log.info("整点计费停车时长计算后;{}", d);
            }
        }
        return d;
    }

  之前也做了一个需求就是计算夜间免费停车时长(晚20点到早上10点这个区间是免费停车的),知道入场时间和出场时间,所以我就搞了一个时间环TimeLoopService的类(类似于时钟或者是手表,时间走是一圈又一圈的转动,在这个类里面给一个入场时长和出场时间就可以算出夜间免费停车时长),有这个反向操作就可以算出夜间停车时长,只不过之前算的是夜间免费停车时长,现在算的是夜间停车时长,根据上面的规则,夜间20点到早8点这个区间是收费的,使用TimeLoopService找到的时间作为夜间停车时长,停车总时长减去夜间停车时长就是白天停车时长。

  DateUtils类:

package com.xxxx;

import cn.hutool.core.collection.CollectionUtil;
import lombok.extern.slf4j.Slf4j;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.List;

@Slf4j
public class DateUtils {

    private static final DateTimeFormatter df1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final DateTimeFormatter df2 = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    private static final DateTimeFormatter df3 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS");
    private static final DateTimeFormatter df4 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");

    private static final DateTimeFormatter df5 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");

    public static final String DAY = "DAY";
    public static final String HOURS = "HOURS";
    public static final String MINUTES = "MINUTES";

    public static long timeSubstract(LocalDateTime startTime, LocalDateTime endTime, String returnType) {
        if (StringUtils.equals(returnType, HOURS)) {
            return Duration.between(startTime, endTime).toHours();
        }
        if (StringUtils.equals(returnType, DAY)) {
            return Duration.between(startTime, endTime).toDays();
        }
        if (StringUtils.equals(returnType, MINUTES)) {
            return Duration.between(startTime, endTime).toMinutes();
        }
        return 0L;
    }

    public static String localDateTimeToString(LocalDateTime localDateTime) {
        String format = df1.format(localDateTime);
        return format;
    }

    public static String localDateTimeToStringToYMD(LocalDateTime localDateTime) {
        String format = df2.format(localDateTime);
        return format;
    }

    public static LocalDateTime stringToLocalDateTime(String dateTimeStr) {
        LocalDateTime localDateTime = LocalDateTime.parse(dateTimeStr, df1);
        return localDateTime;
    }

    public static LocalDateTime stringIncludeTStrToLocalDateTime(String dateTimeStr) {
        log.info("stringIncludeTStrToLocalDateTime入参:{}",dateTimeStr);
        dateTimeStr = dateTimeStr.replace("'T'", " ");
        dateTimeStr = dateTimeStr.substring(0, 19);
        log.info("stringIncludeTStrToLocalDateTime.dateTimeStr处理后的值:{}",dateTimeStr);
        LocalDateTime localDateTime = LocalDateTime.parse(dateTimeStr, df1);
        return localDateTime;
    }

    public static LocalDateTime stringIncludeTStrToLocalDateTime2(String dateTimeStr) {
        log.info("stringIncludeTStrToLocalDateTime入参:{}",dateTimeStr);
        dateTimeStr = dateTimeStr.replace("T", " ");
        dateTimeStr = dateTimeStr.substring(0, 19);
        log.info("stringIncludeTStrToLocalDateTime.dateTimeStr处理后的值:{}",dateTimeStr);
        LocalDateTime localDateTime = LocalDateTime.parse(dateTimeStr, df1);
        return localDateTime;
    }

    public static String localDateTimeToStringT(LocalDateTime localDateTime) {
        String format = df3.format(localDateTime);
        return format;
    }

    public static String localDateTimeToStringT2(LocalDateTime localDateTime) {
        String format = df4.format(localDateTime).replace("T", " ");
        return format;
    }

    public static String localDateTimeToStringT3(LocalDateTime localDateTime) {
        String format = df5.format(localDateTime).replace("T", " ").replace("Z"," ");
        format = format.substring(0,format.lastIndexOf("."));
        return format;
    }


    public static LocalDateTime calculatTime(String time){
        LocalDateTime psTime = null;
        if (org.apache.commons.lang3.StringUtils.isNotEmpty(time) && !(time.indexOf("-") != -1)) {
            String s =time;
            s = s.substring(0, 4) + "-" + s.substring(4, 6) + "-" + s.substring(6, 8) + " " + s.substring(8, 10) + ":" + s.substring(10, 12) + ":" + s.substring(12, 14);
            psTime = DateUtils.stringToLocalDateTime(s);
        } else if (org.apache.commons.lang3.StringUtils.isNotEmpty(time) && (time.indexOf("-") != -1)) {
            String passTime =time;
            if (passTime.contains("T")) {
                psTime = DateUtils.stringIncludeTStrToLocalDateTime(passTime);
            } else {
                psTime = DateUtils.stringToLocalDateTime(passTime);
            }
        }
        return psTime;
    }

    public static List<String>  calculationDays(String startTime, String endTime) {
        List<String> allDays = new ArrayList<>();
        DateTimeFormatter dateTimeFormatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate startDt = LocalDate.parse(startTime, dateTimeFormatter1);
        int start_Y = startDt.getYear();
        int start_M = startDt.getMonth().getValue();
        int start_D = startDt.getDayOfMonth();
        log.info("Y=" + start_Y + ",M=" + start_M + ",D=" + start_D);

        LocalDate endDt = LocalDate.parse(endTime, dateTimeFormatter1);
        int start_Y1 = endDt.getYear();
        int start_M1 = endDt.getMonth().getValue();
        int start_D1 = endDt.getDayOfMonth();
        log.info("Y1=" + start_Y1 + ",M1=" + start_M1 + ",D1=" + start_D1);

        if (startDt.compareTo(endDt) > 1) {
            //开始时间大于结束时间返回空!
            return null;
        }
        String endTimeStr = dateTimeFormatter1.format(endDt);

        Period period = Period.between(LocalDate.parse(startTime), endDt);

        StringBuffer sb = new StringBuffer();
        sb.append(period.getYears()).append(",")
                .append(period.getMonths()).append(",")
                .append(period.getDays());
        log.info("=======时间分量:=======" + sb.toString());

        int py = start_Y1 - start_Y;
        int Y = 12;
        detailYear(allDays, start_Y, py, Y);
        log.info("=======allDays--size()=======" + allDays.size());
        if (CollectionUtil.isNotEmpty(allDays)) {
            for (int i = 0; i < allDays.size(); i++) {
                log.info(allDays.get(i) + "--------allDays------>第" + (i + 1) + "天");
            }
            List<String> okResult = getOkResult(allDays, startTime, endTimeStr);
            System.out.println("=======okResult--size()=======" + okResult.size());
            for (int i = 0; i < okResult.size(); i++) {
                log.info(okResult.get(i) + "--------okResult-------->第" + (i + 1));
            }
            return okResult;
        }
        return null;
    }

    /**
     * 处理整年
     *
     * @param allDays
     * @param start_Y
     * @param py
     * @param y
     */
    private static void detailYear(List<String> allDays, int start_Y, int py, int y) {
        //处理年的天
        for (int i = start_Y; i < start_Y + py + 1; i++) {
            for (int j = 1; j <= y; j++) {
                String fst = "";
                if (j <= 9) {
                    fst = i + "-0" + j + "-01";
                } else {
                    fst = i + "-" + j + "-01";
                }
                int diff_day = getDiff_day(fst);
                for (int k = 1; k <= diff_day + 1; k++) {
                    String d = "";
                    if (j <= 9) {
                        d = i + "-0" + j;
                        if (k <= 9) {
                            d += "-0" + k;
                        } else if (k > 9) {
                            d += "-" + k;
                        }
                    } else if (j > 9) {
                        d = i + "-" + j;
                        if (k <= 9) {
                            d += "-0" + k;
                        } else if (k > 9) {
                            d += "-" + k;
                        }
                    }
                    allDays.add(d);
                }
            }
        }
    }

    /**
     * 获取正确的List
     *
     * @param startTime
     * @param allDays
     * @return
     */
    private static List<String> getOkResult(List<String> allDays, String startTime, String endTime) {
        List<String> result = new ArrayList<>();
        int indexStart = 0;
        int indexEnd = 0;
        for (int i = 0; i < allDays.size(); i++) {
            if (allDays.get(i).equals(startTime)) {
                indexStart = i;
            }
        }
        for (int i = 0; i < allDays.size(); i++) {
            if (allDays.get(i).equals(endTime)) {
                indexEnd = i;
            }
        }
        result = allDays.subList(indexStart, indexEnd + 1);
        return result;
    }

    /**
     * 根据当月第一天计算本月的开始天和结束天
     *
     * @param fst
     * @return
     */
    private static int getDiff_day(String fst) {
        LocalDate fstLd = LocalDate.parse(fst);
        //获取月的第一天
        LocalDate fstLd_fd = fstLd.with(TemporalAdjusters.firstDayOfMonth());
        //获取月的最后一天
        LocalDate fstLd_ld = fstLd.with(TemporalAdjusters.lastDayOfMonth());
        Period period2 = Period.between(fstLd_fd, fstLd_ld);
        int diff_day = period2.getDays();
        return diff_day;
    }

}

  TimeLoopService类:

package xxxx.impl;
import com.xxxxxx.DateUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.HashMap;
import java.util.List;

/**
 * @author zlf
 * @description:
 * @time: 2022/9/7 9:58
 */
@Slf4j
@Data
public class TimeLoopService {

    /**
     * 夜间开始时长点数
     */
    private int start;

    /**
     * 夜间结束时长点数
     */
    private int end;

    /**
     * 入场时间
     */
    private LocalDateTime goInStart;

    /**
     * 入场时钟的开始计费点
     */
    private LocalDateTime goInStartClockTime;

    /**
     * 入场时钟结束计费点
     */
    private LocalDateTime goInEndClockTime;

    /**
     * 入场 minClock 00:00:00
     */
    private LocalDateTime goInPassMinTime;

    /**
     * 入场 maxClock 23:59:59
     */
    private LocalDateTime goInPassMaxTime;

    /**
     * 入场月的最后一天
     */
    private Integer goInMothLastDay;

    /**
     * 出场时间
     */
    private LocalDateTime goOutEnd;

    /**
     * 出场时钟的开始计费点
     */
    private LocalDateTime goOutStartClockTime;

    /**
     * 出场时钟结束计费点
     */
    private LocalDateTime goOutEndClockTime;

    /**
     * 出场 minClock 00:00:00
     */
    private LocalDateTime goOutPassMinTime;

    /**
     * 出场clock 23:59:59
     */
    private LocalDateTime goOutPassMaxTime;

    /**
     * 出场月的第一天
     */
    private Integer goOutMothFirstDay;

    /**
     * 入场时间和出场时间相差几天
     */
    private int lessDay;

    /**
     * 一个时钟周期的免费时长小时数
     */
    private int oneClockFreeHours;

    /**
     * 00:00:00
     */
    private String psMinStr = "00:00:00";

    /**
     * 23:59:59
     */
    private String psMaxStr = "23:59:59";

    /**
     * 00:00
     */
    private String oo = "00:00";

    private HashMap<String, Integer> goInStartMap = new HashMap<>();

    private HashMap<String, Integer> goOutEndMap = new HashMap<>();

    private Boolean flag1 = Boolean.FALSE;


    public TimeLoopService(Integer start, Integer end, LocalDateTime goInStart, LocalDateTime goOutEnd) {
        this.start = start;
        this.end = end;
        this.goInStart = goInStart;
        this.goOutEnd = goOutEnd;

        oneClockFreeHours = 24 - Math.abs((start - end));
        log.info("oneClockFreeHours:{}", oneClockFreeHours);

        DateTimeFormatter dateTimeFormatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        DateTimeFormatter dateTimeFormatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd");

        String gs = dateTimeFormatter1.format(goInStart);
        LocalDate startDt = LocalDate.parse(gs, dateTimeFormatter1);
        int start_Y = startDt.getYear();
        int start_M = startDt.getMonth().getValue();
        int start_D = startDt.getDayOfMonth();
        String[] s = gs.split(" ");
        String[] s2 = s[1].split(":");
        int start_H = Integer.valueOf(s2[0]).intValue();
        int start_MM = Integer.valueOf(s2[1]).intValue();
        int start_S = Integer.valueOf(s2[2]).intValue();
        goInStartMap.put("start_Y", start_Y);
        goInStartMap.put("start_M", start_M);
        goInStartMap.put("start_D", start_D);
        goInStartMap.put("start_H", start_H);
        goInStartMap.put("start_MM", start_MM);
        goInStartMap.put("start_S", start_S);
        log.info("开始时间各分量:Y=" + start_Y + ",M=" + start_M + ",D=" + start_D + ",H=" + start_H + ",MM:" + start_MM + ",start_S:" + start_S);

        String ge = dateTimeFormatter1.format(goOutEnd);
        LocalDate endDt = LocalDate.parse(ge, dateTimeFormatter1);
        int start_Y1 = endDt.getYear();
        int start_M1 = endDt.getMonth().getValue();
        int start_D1 = endDt.getDayOfMonth();
        String[] s3 = ge.split(" ");
        String[] s4 = s3[1].split(":");
        int start_H1 = Integer.valueOf(s4[0]).intValue();
        int start_MM1 = Integer.valueOf(s4[1]).intValue();
        int start_S1 = Integer.valueOf(s4[2]).intValue();
        goOutEndMap.put("start_Y1", start_Y1);
        goOutEndMap.put("start_M1", start_M1);
        goOutEndMap.put("start_D1", start_D1);
        goOutEndMap.put("start_H1", start_H1);
        goOutEndMap.put("start_MM1", start_MM1);
        goOutEndMap.put("start_S1", start_S1);
        log.info("结束时间各分量:Y1=" + start_Y1 + ",M1=" + start_M1 + ",D1=" + start_D1 + ",H1=" + start_H1 + ",MM1:" + start_MM1 + ",start_S1:" + start_S1);

        String startStr = "";
        if (0 < start && start <= 9) {
            startStr = "0" + start;
        } else {
            startStr = start.toString();
        }
        log.info("startStr:{}", startStr);

        String endStr = "";
        if (0 < end && end <= 9) {
            endStr = "0" + end;
        } else {
            endStr = end.toString();
        }
        log.info("endStr:{}", endStr);

        String start_M_str = "";
        if (start_M <= 9) {
            start_M_str = "0" + start_M;
        } else {
            start_M_str = String.valueOf(start_M);
        }
        log.info("start_M_str:{}", start_M_str);

        String start_M_str1 = "";
        if (start_M1 <= 9) {
            start_M_str1 = "0" + start_M1;
        } else {
            start_M_str1 = String.valueOf(start_M1);
        }
        log.info("start_M_str1:{}", start_M_str1);

        String start_D_str = "";
        if (start_D <= 9) {
            start_D_str = "0" + start_D;
        } else {
            start_D_str = String.valueOf(start_D);
        }
        log.info("start_D_str:{}", start_D_str);

        String start_D_str1 = "";
        if (start_D1 <= 9) {
            start_D_str1 = "0" + start_D1;
        } else {
            start_D_str1 = String.valueOf(start_D1);
        }
        log.info("start_D_str1:{}", start_D_str1);

        goInStartClockTime = DateUtils.stringToLocalDateTime(start_Y + "-" + start_M_str + "-" + start_D_str + " " + startStr + ":" + oo);
        log.info("goInStartClockTime:{}", DateUtils.localDateTimeToString(goInStartClockTime));

        goInEndClockTime = DateUtils.stringToLocalDateTime(start_Y + "-" + start_M_str + "-" + start_D_str + " " + endStr + ":" + oo);
        log.info("goInEndClockTime:{}", DateUtils.localDateTimeToString(goInEndClockTime));

        goInPassMinTime = DateUtils.stringToLocalDateTime(start_Y + "-" + start_M_str + "-" + start_D_str + " " + psMinStr);
        log.info("goInPassMinTime:{}", DateUtils.localDateTimeToString(goInPassMinTime));

        goInPassMaxTime = DateUtils.stringToLocalDateTime(start_Y + "-" + start_M_str + "-" + start_D_str + " " + psMaxStr);
        log.info("goInPassMaxTime:{}", DateUtils.localDateTimeToString(goInPassMaxTime));


        goOutStartClockTime = DateUtils.stringToLocalDateTime(start_Y1 + "-" + start_M_str1 + "-" + start_D_str1 + " " + startStr + ":" + oo);
        log.info("goOutStartClockTime:{}", DateUtils.localDateTimeToString(goOutStartClockTime));

        goOutEndClockTime = DateUtils.stringToLocalDateTime(start_Y1 + "-" + start_M_str1 + "-" + start_D_str1 + " " + endStr + ":" + oo);
        log.info("goOutEndClockTime:{}", DateUtils.localDateTimeToString(goOutEndClockTime));

        goOutPassMinTime = DateUtils.stringToLocalDateTime(start_Y1 + "-" + start_M_str1 + "-" + start_D_str1 + " " + psMinStr);
        log.info("goOutPassMinTime:{}", DateUtils.localDateTimeToString(goOutPassMinTime));

        goOutPassMaxTime = DateUtils.stringToLocalDateTime(start_Y1 + "-" + start_M_str1 + "-" + start_D_str1 + " " + psMaxStr);
        log.info("goOutPassMaxTime:{}", DateUtils.localDateTimeToString(goOutPassMaxTime));

        log.info("start_D1:{},start_D:{}", start_D1, start_D);
        lessDay = start_D1 - start_D - 1;
        log.info("计算lessDay = start_D1 - start_D - 1得:{}", lessDay);
        if (lessDay < 0) {
            lessDay = 0;
        }
        log.info("开始时间和结束时间相差几天:lessDay:{}", lessDay);
        //跨月计算
        if (start_M != start_M1 && start_Y == start_Y1) {
            log.info("=========跨越计算开始=========");
            //入场本月的最后一天
            LocalDateTime goInLastDay = goInStart.with(TemporalAdjusters.lastDayOfMonth());
            log.info("goInLastDay:{}", DateUtils.localDateTimeToString(goInLastDay));
            String goInLastDayStr = dateTimeFormatter1.format(goInLastDay);
            LocalDate goInLastDayLd = LocalDate.parse(goInLastDayStr, dateTimeFormatter1);
            goInMothLastDay = goInLastDayLd.getDayOfMonth();
            log.info("goInMothLastDay:{}", goInMothLastDay);
            lessDay = (goInMothLastDay - start_D) + start_D1 - 1;
            log.info("跨月计算开始时间和结束时间相差几天:lessDay:{}", lessDay);
            //出场本月的第一天
            LocalDateTime goOutFirstDay = goOutEnd.with(TemporalAdjusters.firstDayOfMonth());
            log.info("goOutFirstDay:{}", DateUtils.localDateTimeToString(goOutFirstDay));
            String goOutFirstDayStr = dateTimeFormatter1.format(goOutFirstDay);
            LocalDate goOutFirstDayLd = LocalDate.parse(goOutFirstDayStr, dateTimeFormatter1);
            goOutMothFirstDay = goOutFirstDayLd.getDayOfMonth();
            log.info("goOutMothFirstDay:{}", goOutMothFirstDay);
            //入场所在天是否是本月的最后一天 且出场天是否是本月第一天
            if (start_D == goInMothLastDay && 1 == goOutMothFirstDay) {
                flag1 = Boolean.TRUE;
            }
            log.info("=========跨越计算结束=========");
        }
        //跨年
        if (start_Y1 - start_Y >= 1) {
            String startStr1 = startDt.format(dateTimeFormatter2);
            String enStr1 = endDt.format(dateTimeFormatter2);
            log.info("startStr1:{}", startStr1);
            log.info("enStr1:{}", enStr1);
            List<String> res = DateUtils.calculationDays(startStr1, enStr1);
            log.info("跨年两个时间相隔几天:{}", res.size());
            lessDay = res.size() - 2;
            log.info("跨年两个时间相隔几天减2后得:{}", lessDay);
            if (lessDay < 0) {
                lessDay = 0;
            }
        }

    }


    public Double TotalFreeTime() {
        //开始大于结束 开始等于结束
        if (goInStart.compareTo(getGoOutEnd()) >= 0) {
            return 0d;
        }
        int start_D = goInStartMap.get("start_D");
        int start_D1 = goOutEndMap.get("start_D1");
        log.info("start_D:{},start_D1:{}", start_D1, start_D);
        // 进的时间顺时针找,出的时间逆时针找
        if (lessDay == 0 && start_D == start_D1) {
            if (goInStart.compareTo(goInEndClockTime) >= 1 && goOutEnd.compareTo(goOutStartClockTime) <= 0) {
                log.info("================ 进出都在没落在计费区间直接返回0 ===========");
                return 0d;
            }
            // 一个时钟周期
            if (goInStart.compareTo(goInPassMinTime) == 0 && goOutEnd.compareTo(goOutPassMaxTime) == 0) {
                int ocf = oneClockFreeHours * 60;
                log.info("ocf:{}", ocf);
                return Double.valueOf(ocf);
            }
            log.info("================ 进出都在 同侧 (2种情况)开始 ===========");
            // 大于等于 00:00:00 小于等于 本个时钟的结束计费点 goOutEndClockTime
            if (goInStart.compareTo(goInPassMinTime) >= 0 && goOutEnd.compareTo(goOutEndClockTime) <= 0) {
                Duration between = Duration.between(goInStart, goOutEnd);
                long t1 = between.toMinutes();
                log.info("t1:{}", t1);
                return Double.valueOf(String.valueOf(t1));
            }
            // 大于等于 本个时钟的开始计费点 goInStartClockTime 小于等于 23:59:59
            if (goInStart.compareTo(goInStartClockTime) >= 0 && goOutEnd.compareTo(goInPassMaxTime) <= 0) {
                Duration between = Duration.between(goInStart, goOutEnd);
                long t2 = between.toMinutes();
                log.info("t2:{}", t2);
                return Double.valueOf(String.valueOf(t2));
            }
            log.info("================ 进出都在 同侧 (2种情况)结束 ===========");

            log.info("================ 进出都有交叉 分割的情况 (3种情况)开始 ===========");
            if (goInStart.compareTo(goInPassMinTime) >= 0
                    && goInStart.compareTo(goInEndClockTime) == -1
                    && goOutEnd.compareTo(goInStartClockTime) <= 0
                    && goOutEnd.compareTo(goInEndClockTime) >= 0) {
                Duration between = Duration.between(goInStart, goInEndClockTime);
                long t3 = between.toMinutes();
                log.info("t3:{}", t3);
                return Double.valueOf(String.valueOf(t3));
            }
            if (goOutEnd.compareTo(goInPassMaxTime) <= 0
                    && goOutEnd.compareTo(goInStartClockTime) == 1
                    && goInStart.compareTo(goInEndClockTime) >= 0
                    && goInStart.compareTo(goInStartClockTime) <= 0) {
                Duration between = Duration.between(goInStartClockTime, goOutEnd);
                long t4 = between.toMinutes();
                log.info("t4:{}", t4);
                return Double.valueOf(String.valueOf(t4));
            }
            if (goInStart.compareTo(goInPassMinTime) == 1
                    && goInStart.compareTo(goInEndClockTime) == -1
                    && goOutEnd.compareTo(goInPassMaxTime) == -1
                    && goOutEnd.compareTo(goInStartClockTime) == 1) {
                Duration between = Duration.between(goInStart, goInEndClockTime);
                long t5 = between.toMinutes();
                log.info("t5:{}", t5);
                Duration between2 = Duration.between(goInStartClockTime, goOutEnd);
                long t6 = between2.toMinutes();
                log.info("t6:{}", t6);
                long t7 = t5 + t6;
                log.info("t7:{}", t7);
                return Double.valueOf(String.valueOf(t7));
            }
            log.info("================ 进出都有交叉 分割的情况 (3种情况)结束 ===========");
        } else if (lessDay >= 1 || (start_D1 - start_D) == 1 || flag1) {
            Double heard = this.calculateHeardClock();
            Double between = this.calculateBetweenClock();
            Double tail = this.calculateTailClock();
            log.info("heard:{},between:{},tail:{}", heard, between, tail);
            Double threeSum = heard + between + tail;
            log.info("threeSum:{}", threeSum);
            return threeSum;
        }
        return 0d;
    }

    private Double calculateHeardClock() {
        if (goInStart.compareTo(goInPassMaxTime) == 0) {
            log.info("heard return 0");
            return 0d;
        }
        if (goInStart.compareTo(goInPassMinTime) == 0) {
            int t8 = oneClockFreeHours * 60;
            log.info("t8:{}", t8);
            return Double.valueOf(t8);
        }
        if (goInStart.compareTo(goInPassMinTime) == 1
                && goInStart.compareTo(goInEndClockTime) == -1) {
            Duration between2 = Duration.between(goInStart, goInEndClockTime);
            long t9 = between2.toMinutes();
            log.info("t9:{}", t9);
            Duration between3 = Duration.between(goInStartClockTime, goInPassMaxTime);
            long t10 = between3.toMinutes();
            log.info("t10:{}", t10);
            long t11 = t9 + t10;
            return Double.valueOf(t11);
        }
        if (goInStart.compareTo(goInEndClockTime) <= 0
                && goInStart.compareTo(goInEndClockTime) >= 0) {
            Duration between4 = Duration.between(goInStartClockTime, goInPassMaxTime);
            long t12 = between4.toMinutes();
            log.info("t12:{}", t12);
            return Double.valueOf(t12);
        }
        if (goInStart.compareTo(goInStartClockTime) == 1
                && goInStart.compareTo(goInPassMaxTime) <= 0) {
            Duration between4 = Duration.between(goInStart, goInPassMaxTime);
            long t19 = between4.toMinutes();
            log.info("t19:{}", t19);
            return Double.valueOf(t19);
        }
        if (goInStart.compareTo(goInEndClockTime) >= 0
                && goInStart.compareTo(goInStartClockTime) <= 0) {
            Duration between4 = Duration.between(goInStartClockTime, goInPassMaxTime);
            long t19 = between4.toMinutes();
            log.info("t19:{}", t19);
            return Double.valueOf(t19);
        }
        return 0d;
    }

    private Double calculateBetweenClock() {
        Double bt = Double.valueOf(lessDay * oneClockFreeHours * 60);
        log.info("bt:{}", bt);
        return bt;
    }

    private Double calculateTailClock() {
        if (goOutEnd.compareTo(goOutPassMinTime) == 0) {
            log.info("tail return 0");
            return 0d;
        }
        if (goOutEnd.compareTo(getGoOutPassMaxTime()) == 0) {
            int t13 = oneClockFreeHours * 60;
            log.info("t13:{}", t13);
            return Double.valueOf(t13);
        }
        if (goOutEnd.compareTo(goOutPassMinTime) == 1
                && goOutEnd.compareTo(goOutEndClockTime) == -1) {
            Duration between6 = Duration.between(goOutPassMinTime, goOutEnd);
            long t14 = between6.toMinutes();
            log.info("t14:{}", t14);
            return Double.valueOf(t14);
        }
        if (goOutEnd.compareTo(goOutEndClockTime) >= 0
                && goOutEnd.compareTo(goOutStartClockTime) <= 0) {
            Duration between7 = Duration.between(goOutPassMinTime, goOutEndClockTime);
            long t15 = between7.toMinutes();
            log.info("t15:{}", t15);
            return Double.valueOf(t15);
        }
        if (goOutEnd.compareTo(goOutPassMaxTime) == -1
                && goOutEnd.compareTo(goOutStartClockTime) == 1) {
            Duration between8 = Duration.between(goOutStartClockTime, goOutEnd);
            long t16 = between8.toMinutes();
            log.info("t16:{}", t16);

            Duration between9 = Duration.between(goOutPassMinTime, goOutEndClockTime);
            long t17 = between9.toMinutes();
            log.info("t17:{}", t17);

            long t18 = t16 + t17;
            log.info("t18:{}", t18);
            return Double.valueOf(t18);
        }
        return 0d;
    }

}

  图形化理解如下:

图片

  这个实现基本上是没有啥问题的,如果哪位小伙伴发现上面存在的bug的话请联系我,或者有其它的实现可以联系我,把你的实现发给我参考和学习下。

2.新姿势

  一般的业务数据处理都是借助定时任务加MybatisPlus持久层框架来批量处理数据到一张表中,定时任务有选型参看,然后在这种表由于业务需要新增了一些冗余字段,接着需要用定时任务跑批处理填充这些冗余的字段,会使用到mybatisPlus的IService层的saveBatch、saveOrUpdateBatch、updateBatchById,或者是使用Mapper层的updateById在一个for循环里面根据主键更新一批数据(或者可以在for中使用mybatisPlus的sql注入器alwaysUpdateSomeColumnById,这种方式比上面的Iservice层或者是Mapper层原生的api要快),这种操作拿1-2万条数据来测试,性能真的是会很慢很慢,如果数据量太大查询的数据过的的话会导致OOM,如果是在一段代码中查先把表的数据查出来,然后在使用多线程更新了一些数据就会导致多线程并发的问题,因为MyBatisPlus的sqlSession也是不是线程安全的,所以多线程批量更新是不靠谱的,就不要用这种姿势了,如果你想用线程批量更新除非你要处理好并发问题、事务问题,避免会出现一些稀奇古怪的问题,一般更新都是在一个主线程里面一撸到底。

  mybatisPlus批量插入优化可以参考我之前写的一篇文章:mybatisPlus批量插入优化,性能快的飞起

  数据处理可以使用JDK的Stream流提高数据计算处理的速度;

  使用mybatisPlus批量插入数据最坑的两点就是主键不唯一和重复跑批数据重复性校验的问题;

   查询数据量大:mybatisPlus流式查询,也叫游标查询,客户端应用和mysql数据库建立连接后,通过这种查询方式是利用mysql的游标,少量多批次的方式避免了一次性将大量的数据查询加载到JVM的堆内存中;
   主键不唯一: 业务数据的主键id必须是全局唯一的,可以使用ID生成器生成,保证其全球唯一,主键不唯一,跑批多次每一次的保存的数据结果会变少了,之前我就遇到这种情况了;

  数据重复性校验: 首先要保证处理入库的数据的id是唯一的,比如财务对账单的数据处理,可以使用业务测的交易流水号作为财务对账数据的主键,如果某一天或者某一批数据又问题,可以将有问题的数据删除,然后重新跑批入库,避免数据重复(多次跑批导致数据重复的问题)。

  可以采用ES来保存处理好的数据:

  MyBatisPlus多数据源加ES大宽表架构落地实践

  这种方式的好处是:

  1.降低mysql(行式-支持事务)数据库的压力

  如果有很多定时任务的跑批的数据处理都是使用mysql来存储批处理的结果数据,无论是在哪个时段都会拉胯mysql的数据库,如果是在业务高峰期的话一个处理不恰当导致mysql数据宕机或者是在业务高峰期造成系统会变慢。

  2.避免数据重复

  只要保证ID是唯一的,更新ES中的数据是先将之前的数据删除后在插入,充分利用ES的大宽表的思想和ES的海量数据存储和搜索的能力,采用Mysql + ES来处理分析查询业务数据。

  本文中遇到的是使用两个表来分开处理业务数据,一个源数据表(源数据也可以使业务查询出来需要计算处理的数据),一个结果数据表(使用mysql就会遇到在一个结果数据表里面批量更新的之前的数据结果,所以使用mybatisPlus的api就会很慢,可以使用在for循环中mybatisPlus的sql注入器alwaysUpdateSomeColumnById,或者是将有问题的数据先删除,然后使用mybatisPlus的sql注入器InsertBatchSomeColumn批量插入数据这种就会很快,或者可以将处理的结果数据存到ES里面),这几个点就可以让我们处理业务数据快到起飞,好的设计和实现才能给业务带来价值,差的设计和实现只会给业务代码拖累和负增长,这种简单的业务数据且数据量不大可以通过过写业务代码实现,如果数据量确实是太大了要使用大数据领域的相关解决方案了或者一些其它的解决方案了。

3.总结

  通过记录我在项目中想遇到的问题和一些奇葩的需求是如何解决和实现的跟大家做一个分享,场景化学习我觉得是一个比较好的学习方式,也是比较深刻的方式,学以致用,解决实际的问题,在学习中实践,在实践中学习,同时也是一次总结,希望我的分享对你有所帮助,请一键三连,点赞收藏加关注,么么哒!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/579438.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MongoDB—Indexes

一、Indexes 索引支持在MongoDB中高效执行查询。没有 索引&#xff0c;MongoDB必须执行集合扫描&#xff0c;即扫描每个 集合中的文档&#xff0c;以选择与 查询语句。如果查询存在适当的索引&#xff0c; MongoDB可以使用索引来限制它必须的文档数量 检查。 索引是特殊的数据…

近期群晖容器查询注册表失败,无法下载镜像

前言 本以为是群晖的问题&#xff0c;后来发现大家也都遇到了类似的问题&#xff0c;查询注册表出现错误&#xff0c;无法搜索和下载docker镜像&#xff0c;本期就来介绍几个可平替的方案。 方案一&#xff1a;SSH拉取 使用SSH拉取首先要打开群晖的SSH功能&#xff1a;在【…

数据结构(C语言):顺序循环队列的基本操作

一、题目 设队列的元素类型为char&#xff0c;实现顺序循环队列的各种基本操作的程序&#xff1a; ① 初始化队列Q&#xff1b; ② 判断队列Q是否为空&#xff1b; ③ 入队操作。循环调用入队操作&#xff0c;将若干元素&#xff08;不少于10个&#xff09;入队&#xff1b…

LeetCode 49 字母异位词分组

LeetCode 49 字母异位词分组 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;https://leetcode.cn/problems/group-anagrams/description/ 博主Github&#xff1a;https://github.com/GDUT-Rp/LeetCode 题目&#xff1a; 给你一个字符串数组&#x…

如何使用ChatGPT?初学者简单指南

Chat GPT 风靡全球&#xff0c;并在短时间内成为世界上增长最快的应用程序。随着所有关于其潜在影响的讨论&#xff0c;你可能会想知道&#xff1a;如何使用 ChatGPT&#xff1f; 使用 ChatGPT需要准备&#xff1a;一个 OpenAI 帐户和一个网络浏览器。 访问chat.openai.com并…

ICV报告:中国的数字经济与5G市场研究报告

近日&#xff0c;专注于前沿科技领域的国际咨询机构ICV发布了《中国的数字经济与5G市场研究报告》。报告指出&#xff0c;随着5G商用的发展&#xff0c;5G对经济社会的影响逐步显现&#xff0c;其影响突出体现在对数字产业发展的带动上。随着5G应用的不断创新与扩散&#xff0c…

网络公开课1

网络公开课1 概念* 常见面试题 输入URL发生哪些事 https http1.0 2.0的区别…… OSI七层模型 协议DNS 传输层 三次握手 四次挥手 http 物理层&#xff1a;并不是指光纤电缆那些&#xff0c;那些是设备。只关心如何传输数据&#xff0c;传输的是比特流 0 1。 应用层&#xff1a;…

汇编物理地址计算与段地址认识

1.物理地址概念: 所有内存单元构造的存储空间是一个一维性空间.每个内存单元在这个空间都有一个唯一的地址,这个唯一的地址称为物理地址. CPU通过地址总线送入存储器的必须是一个内存单元的物理地址. 不同的CPU有不同形式的物理地址形成方式. 物理地址计算公式: 物理地址 …

微服务的演变史(单体式、分布式、SOA架构、微服务架构)

单体式架构 从架构层面来考虑:一个应用只部署一个服务,或该应用由多个服务组成的时候,只部署在一台服务器上 把一种系统的所有功能全部耦合在一个应用中的框架方式 这种开发方式简单,但是只适合体量较小的业务,一旦业务体量增加到一定程度的时候,单机的硬件资源将没办法满足…

JOSEF约瑟 JDL-1001A 电流继电器 导轨式安装 批发价格

名称&#xff1a;电流继电器品牌&#xff1a;JOSEF约瑟型号&#xff1a;JDL-1001A触点容量&#xff1a;250V2A整定范围&#xff1a;0.03-9.99A返回系数&#xff1a;≥0.9特点&#xff1a;交直流两用、消耗小、返回系数高。 型号说明 JD L -1 XXX X/ XXX / XXX 系列型号 JDL…

首发Yolov5/Yolov7优化:Adam该换了!斯坦福最新Sophia优化器,比Adam快2倍 | 2023.5月斯坦福最新成果

1.Sophia优化器介绍 斯坦福2023.5月发表的最新研究成果,他们提出了「一种叫Sophia的优化器,相比Adam,它在LLM上能够快2倍,可以大幅降低训练成本」。 论文:https://arxiv.org/pdf/2305.14342.pdf 本文介绍了一种新的模型预训练优化器:Sophia(Second-order Clippe…

vue2_模版语法

目录 模版语法 react用jsx语法编译后的null作用 插值表达式{{}} v-bind和{{}} 关于国内谷歌自带翻译停用如何解决&#xff08;额外&#xff09; 会一点的插值表达式&#xff0c;也有限制 模版语法 更接近原生js的写法jsx语法 jsx是react提出的&#xff1b;后很多前端框架…

LeetCode 周赛 347(2023/05/28)二维空间上的 LIS 最长递增子序列问题

本文已收录到 AndroidFamily&#xff0c;技术和职场问题&#xff0c;请关注公众号 [彭旭锐] 提问。 往期回顾&#xff1a;LeetCode 单周赛第 346 场 仅 68 人 AK 的最短路问题 周赛 347 概览 T1. 移除字符串中的尾随零&#xff08;Easy&#xff09; 标签&#xff1a;模拟、…

HTTP协议深入理解+如何使用Fiddler抓包

博主简介&#xff1a;想进大厂的打工人博主主页&#xff1a;xyk:所属专栏: JavaEE初阶 目录 文章目录 一、HTTP概述 1.1 什么是HTTP 1.2 理解应用层协议 二、抓包工具fiddler的使用 2.1 几个需要注意的点 2.2 fiddler的原理 2.3 fiddler的使用技巧 三、HTTP请求&#xff08;Re…

intel驱动程序和支持助理常见问题:不识别、无法检测等问题解决方法

起因&#xff1a; wifi驱动有点问题&#xff0c;于是想着更新一下官方的驱动&#xff0c;下载intel驱动程序和支持助理并安装完成后&#xff0c;打开成了这个样子&#xff0c;刷新多少次都没有用&#xff0c;就是不识别。 解决方法&#xff1a; 经过一波胡乱操作&#xff0…

华为OD机试之真正的密码(Java源码)

真正的密码 题目描述 一行中输入一个字符串数组&#xff0c;如果其中一个字符串的所有以索引0开头的子串在数组中都有&#xff0c;那么这个字符串就是潜在密码在所有潜在密码中最长的是真正的密码&#xff0c;如果有多个长度相同的真正的密码&#xff0c;那么取字典序最大的为…

使用CompletionService进行多个文件打包为zip下载

最近没怎么写博客了&#xff0c;因为前段时间在准备软考复习&#xff0c;昨天考完试&#xff0c;现在总算轻松一点了&#xff0c;有更多自由的时间了&#xff0c;总结一下JUC包下的一些并发工具类&#xff0c;主要是从使用场景入手。 CompletionService可以用于实现任务并行化…

数据结构入门(C语言版)图的概念和功能函数实现

图的概念和功能函数实现 前言1.图的定义和术语1.1 定义1.2 常用术语 2.图的存储结构2.1 图的数组(邻接矩阵)存储表示2.2 图的邻接表存储表示 3.图的遍历3.1 深度优先搜索3.2 广度优先搜索3.3 示例 4.连通网的最小生成树4.1 克鲁斯卡尔(Kruskal)算法4.2 普里姆(Prim)算法 5.图的…

企业云盘软件世界排行榜:提升企业文件管理效率的最佳工具

企业云盘是一种面向企业用户提供的在线存储和文件分享服务&#xff0c;它能够帮助企业实现数据备份、文件共享、办公协同等多种功能。通过企业云盘&#xff0c;企业可以将数据集中管理&#xff0c;避免了传统存储方式的不便和风险。 企业云盘的优势 1. 集中管理&#xff1a;企业…

【js】对象属性的拦截和Proxy代理与Reflect映射的用法与区别

✍️ 作者简介: 前端新手学习中。 &#x1f482; 作者主页: 作者主页查看更多前端教学 &#x1f393; 专栏分享&#xff1a;css重难点教学 Node.js教学 从头开始学习 ajax学习 文章目录 对象属性的拦截介绍SetGet 对象的拦截介绍使用对象属性拦截和对象拦截区别练习题 映射…