Redis 位图实现签到之长时间未签到预警

#目前通行系统项目中有一个新需求【通过对通行记录数据定时分析,查询出长时间没 有刷卡/刷脸通行的学生】

#一看到通行签到相关,就想到了redis的位图,理由也有很多帖子说明了,最大优点占用空间小。

一.redis命令行

SETBIT:设置指定偏移量上的位值。语法:SETBIT <key> <offset> <value>

使用中key可以为  xxx前缀 + + 用户编号 + + yyyyMM 的格式,设置该人员在该月的签到情况。

当执行以下语句获得一个字符时

假如为2024年10月2号,则执行setbit sign:001:202410 1 1 。偏移量为 1 。

 

获取当前key的值为 @ 

使用  字符串二进制转换 得知,二进制为 0100000 ,八位数字,第二位为 1 ,代表2号签到过。

GETBIT:获取指定偏移量上的位值。语法:GETBIT <key> <offset>

BITCOUNT:统计指定键的位中设置为1的位数。语法:BITCOUNT <key> 

BITPOS:找到第一个设置为1的位。语法:BITPOS <key> <offset> 

也可以使用get / set 直接对整个位图进行设置。 

二.代码实现

1.设置指定key的位图

在项目中,使用中key可以为  xxx前缀 + :+ 用户编号 + :+ yyyyMM 的格式,设置该人员在该月的签到情况。

redisTemplate.opsForValue().setBit(key, offset, value);

参数说明:

  • key:要操作的 Redis 键。
  • offset:要设置的位的偏移量(0-based index),即从零开始的位位置。
  • value:要设置的布尔值,true 表示设置为 1false 表示设置为 0
redisTemplate.opsForValue().setBit("sign:3123000901:202411" , 11 ,true);

 

 2.获取查询时间段内的所有月份(后续用到)
/**
     * 统计时间段内的所有月份
     * @param startDate 开始时间,格式为yyyy-MM-dd
     * @param endDate 结束时间,格式为yyyy-MM-dd
     * @return 包含所有月份的列表,格式为yyyyMM
     */
    public static List<String> getMonthsInRange(String startDate, String endDate) {
        // 定义日期格式化器
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        DateTimeFormatter monthFormatter = DateTimeFormatter.ofPattern("yyyyMM");

        // 解析开始和结束日期
        LocalDate start = LocalDate.parse(startDate, dateFormatter);
        LocalDate end = LocalDate.parse(endDate, dateFormatter);

        // 保存所有月份的列表
        List<String> months = new ArrayList<>();

        // 逐月增加,直到超过结束日期
        LocalDate current = start.withDayOfMonth(1); // 从当月第一天开始
        while (!current.isAfter(end)) {
            months.add(current.format(monthFormatter));
            current = current.plusMonths(1); // 增加一个月
        }

        return months;
    }
3.已知获取位图时是byte[] ,将 byte[] 转换为二进制字符串(后续用到)
/**
     * 将 byte[] 转换为二进制字符串
     * @param bytes 字节数组
     * @return 二进制字符串
     */
    public static String byteArrayToBinaryString(byte[] bytes) {
        StringBuilder binaryString = new StringBuilder();
        for (byte b : bytes) {
            // 将每个字节转换为二进制字符串,并补齐为8位
            String binary = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
            binaryString.append(binary);
        }
        return binaryString.toString();
    }
4.将字符串按0补齐到指定长度,补齐到该月份的天数(后续用到)
/**
     * 将字符串按0补齐到指定长度
     * @param input 原始字符串
     * @param length 目标长度
     * @return 补齐后的字符串
     */
    public static String padStringWithZeros(String input, int length) {
        StringBuilder paddedString = new StringBuilder(input);
        while (paddedString.length() < length) {
            paddedString.append("0");
        }
        return paddedString.toString();
    }
5.计算最长连续未通行天数,按1切割(后续用到)
/**
     * 计算最长连续未通行天数
     * @param bitmap 通行记录的位图值
     * @return 最长连续的0序列长度
     */
    public static int longestZeroSequence(String bitmap) {
        // 分割字符串并找出最长的连续零序列
        String[] zeroSequences = bitmap.split("1");
        int longestZeroLength = 0;
        for (String seq : zeroSequences) {
            longestZeroLength = Math.max(longestZeroLength, seq.length());
        }
        return longestZeroLength;
    }
6.具体实现代码
public List<DevPassRecord> passWarning(DevQueryParam param) {
        //计算时间段
        List<String> months = getMonthsInRange(param.getStartTime(), param.getEndTime());
        //获取该辅导员下所有学生id 例 3123000067    sql语句查询
        List<String> list = Arrays.asList("3123000067" , "3123000901");

        Integer a = Integer.valueOf(param.getStartTime().substring(8, 10));
        // 使用 DateTimeFormatter 解析 yyyy-MM-dd 格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        YearMonth yearMonth = YearMonth.parse(param.getEndTime(), formatter);
        // 返回该月份的天数
        int i1 = yearMonth.lengthOfMonth();
        //获取查询时间段中开始时间的天数(后续用到)
        Integer b = i1 - Integer.valueOf(param.getEndTime().substring(8, 10));

        StringBuilder ids = new StringBuilder();
        for (int i = 0; i < list.size(); i++){
            //该用户时间段内,通行记录字符串
            String dayStr = "";
            for (String m : months) {
                //查询redis中该key的位图
                String key = "sign:" + list.get(i) + ":" + m;
                byte[] bitmapBytes = redisTemplate.getConnectionFactory().getConnection().get(key.getBytes());
                int daysInMonth = getDaysInMonth(m);
                if(Objects.equals(bitmapBytes , null)) {
                    StringBuilder sb = new StringBuilder(daysInMonth);
                    for (int j = 0; j < daysInMonth; j++) {
                        sb.append('0');
                    }
                    dayStr = dayStr + sb;
                }else {
                    String s = byteArrayToBinaryString(bitmapBytes);
                    String s1 = padStringWithZeros(s, daysInMonth);
                    dayStr = dayStr + s1;
                }

            }

            //总长度 减去 开始月份未统计的天数 减去  结束时间未统计的天数 为最终统计情况。
            dayStr = dayStr.substring(a-1, dayStr.length() - b);
            System.out.println("总---" + dayStr);
            int num = longestZeroSequence(dayStr);
            System.out.println("最长天数---" + num);
            if(num >= param.getDays()){
                //大于参数天数的人员id,则视为长时间未通行,需要预警
                ids.append(list.get(i)).append(",");
            }
        }
        System.out.println("------>" + ids);

        return null;
    }

三.运行结果

例如分别设置偏离量为   3 ,11,12,18.分别对应代表4号,12号,13号,19号签到。

参数:查询1号到20号的签到情况,天数超过2天时预警。

结果:字符串总长度为20,分别在第4,12,13,19的位置为1,代表签到了。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python mac vscode 脚本文件的运行

切换到脚本文件的目录下 路径的修改 当前文件组织形式&#xff1a; 脚本文件在文件夹下&#xff1a; 赋予权限&#xff1a;chmod x ./scripts/fscd_test.sh 运行&#xff1a;./scripts/fscd_test.sh

人脑与机器连接:神经科技的伦理边界探讨

内容概要 在当今科技飞速发展的时代&#xff0c;人脑与机器连接已成为一个引人注目的前沿领域。在这一背景下&#xff0c;神经科技的探索为我们打开了一个全新的世界&#xff0c;从脑机接口到人工智能的飞跃应用&#xff0c;不仅加速了技术的进步&#xff0c;更触动了我们内心…

[论文阅读] | 智能体长期记忆

更新记录&#xff1a; 2024.11.2 人大高瓴长期记忆综述 文章目录 人大高瓴长期记忆综述智能体与环境交互记忆的来源/形式/操作来源&#xff1a;(1)当前任务历史信息 (2)其他任务的信息 (3)外部知识形式&#xff1a;如何表达记忆的内容&#xff0c;通过(1)文本 (2)参数(训练到模…

R6:LSTM实现糖尿病探索与预测

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 一、实验目的&#xff1a; 学习使用LSTM对糖尿病进行探索预测 二、实验环境&#xff1a; 语言环境&#xff1a;python 3.8编译器&#xff1a;Jupyter notebook…

基于SSM+小程序的计算机实验室排课与查询管理系统(实验室2)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 1、管理员功能有个人中心&#xff0c;学生管理&#xff0c;教师管理&#xff0c;实验室信息管理&#xff0c;实验室预约管理&#xff0c;取消预约管理&#xff0c;实验课程管理&#xff0…

软件项目实施方案,实施计划,总体实施管理方案(word原件)

一、 概述 二、 项目介绍 2.1 概览 三、 项目实施 3.1 项目实施概况 3.2 项目实施管理原则 3.3 项目组织结构 3.4 项目团队 四、 项目实施计划 4.1 项目实施工作流程 4.2 项目软件部分进度安排 4.3 网络拓扑图 4.4 服务器需求清单 五、 人员培训 5.1 培训内容 5…

【日记】第一次觉得 “届く” 是一个很难的东西(1528 字)

正文 昨天晚上吃饭路上&#xff0c;听到喵喵叫。四处张望了一下&#xff0c;看见一只野猫。天黑&#xff0c;看不清具体什么样子。我冲它喵喵叫&#xff0c;试图走近它。跑掉了。 看来我还是不讨小动物喜欢呢&#xff08;笑。 一大早去了医院。这次膝盖看了三个医生。 放射科报…

【Python单元测试】pytest框架单元测试 配置 命令行操作 测试报告 覆盖率

单元测试&#xff08;unit test&#xff09;&#xff0c;简称UT。本文将介绍在Python项目中&#xff0c;pytest测试框架的安装&#xff0c;配置&#xff0c;执行&#xff0c;测试报告与覆盖率 pytest简介 pytest是一款流行的&#xff0c;简单易上手的单元测试框架&#xff0c;…

三次样条插值算法及推导过程

目录 1、定义 2、已知条件求解 3、具体推导 4、matlab案例 5、案例结果 6、matlab仿真 1、定义 给定 n 1 n1 n1个数据点&#xff0c;共有 n n n个区间&#xff0c;三次样条方程 S ( n ) S(n) S(n)满足以下条件&#xff1a;在每个分段区间内 ( x i , x i 1 ) (x_i,x_{i1}) (…

C#与C++结构体的交互

C#在和C进行交互时&#xff0c;有时候会需要传递结构体。 做一些总结&#xff0c;避免大家在用的时候踩坑。 一般情况 例如我们在C里定义了一个struct_basic结构体 1 struct struct_basic 2 { 3 WORD value_1; 4 LONG value_2; 5 DWORD value_3; 6 UINT v…

Flutter Color 大调整,需适配迁移,颜色不再是 0-255,而是 0-1.0,支持更大色域

在之前的 3.10 里&#xff0c; Flutter 的 Impeller 在 iOS 上支持了 P3 广色域图像渲染&#xff0c;但是当时也仅仅是当具有广色域图像或渐变时&#xff0c;Impeller 才会在 iOS 上显示 P3 的广色域的颜色&#xff0c;而如果你使用的是 Color API&#xff0c;会发现使用的还是…

Android平台RTSP转RTMP推送之采集麦克风音频转发

技术背景 RTSP转RTMP推送&#xff0c;好多开发者第一想到的是采用ffmpeg命令行的形式&#xff0c;如果对ffmpeg比较熟&#xff0c;而且产品不要额外的定制和更高阶的要求&#xff0c;未尝不可&#xff0c;如果对产品稳定性、时延、断网重连等有更高的技术诉求&#xff0c;比较…

【十九周】文献阅读:图像识别的深度残差学习

目录 摘要Abstract图像识别的深度残差学习研究背景研究动机解决办法Residual LearningShortcut Connections网络结构 实验结果代码实践论文原文总结 摘要 在之前对神经网络的基础学习中&#xff0c;师兄推荐了我去了解一下 ResNet。因此本周对 ResNet 的开山之作—Deep Residu…

MATLAB/Simulink学习|在Simulink中调用C语言-01使用C Function 实现比例运算

前面的博客中&#xff0c;提到如果想将Simulink仿真推进至硬件实验&#xff0c;需要将积木式的仿真搭建&#xff0c;变换成C语言实现&#xff0c;那么如何在Simulink中验证C代码的正确性呢&#xff1f;我将一边学习&#xff0c;一边更新&#xff0c;一边比较不同方法实现C语言&…

基于BP神经网络的手写体数字图像识别

基于BP神经网络的手写体数字图像识别 摘要 在信息化飞速发展的时代&#xff0c;光学字符识别是一个重要的信息录入与信息转化的手段&#xff0c;其中手写体数字的识别有着广泛地应用&#xff0c;如&#xff1a;邮政编码、统计报表、银行票据等等&#xff0c;因其广泛地应用范围…

分享一个免费的网页转EXE的工具

HTML2EXE是一款在Windows系统下将Web项目或网站打包成EXE执行程序的免费工具。这款工具能够将单页面应用、传统HTMLJavaScriptCSS生成的网站、Web客户端&#xff0c;以及通过现代前端框架&#xff08;如Vue&#xff09;生成的应用转换成独立的EXE程序运行。它支持将任何网站打包…

ubuntu安装与配置Nginx(2)

1. 配置 Nginx Nginx 的配置文件通常位于 /etc/nginx/nginx.conf&#xff0c;而虚拟主机的配置文件通常在 /etc/nginx/sites-available/ 和 /etc/nginx/sites-enabled/ 目录中。 在/etc/nginx/conf.d目录下新建xx.conf文件&#xff0c;配置文件&#xff0c; nginx -t 检查语法…

C++_day2

目录 1. 引用 reference&#xff08;重点&#xff09; 1.1 基础使用 1.2 特性 1.3 引用参数 2. C窄化&#xff08;了解&#xff09; 3. 输入&#xff08;熟悉&#xff09; 4. string 字符串类&#xff08;掌握&#xff09; 4.1 基础使用 4.2 取出元素 4.3 字符串与数字转换 5. …

JAVA WEB — HTML CSS 入门学习

本文为JAVAWEB 关于HTML 的基础学习 一 概述 HTML 超文本标记语言 超文本 超越文本的限制 比普通文本更强大 除了文字信息 还可以存储图片 音频 视频等标记语言 由标签构成的语言HTML标签都是预定义的 HTML直接在浏览器中运行 在浏览器解析 CSS 是一种用来表现HTML或XML等文…

第十五章 Vue工程化开发及Vue CLI脚手架

目录 一、引言 二、Vue CLI 基本介绍 三、安装Vue CLI 3.1. 安装npm和yarn 3.2. 安装Vue CLI 3.3. 查看 Vue 版本 四、创建启动工程 4.1. 创建项目架子 4.2. 启动工程 五、脚手架目录文件介绍 六、核心文件讲解 6.1. index.html 6.2. main.js 6.3. App.vue 一、…