利用Redis bitmap 實現簽到案例

數據庫實現

設計簽到功能對應的數據庫表

 CREATE TABLE `sign_record` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主鍵',`user_id` bigint NOT NULL COMMENT '用戶id',`year` year NOT NULL COMMENT '簽到年份',`month` tinyint NOT NULL COMMENT '簽到月份',`date` date NOT NULL COMMENT '簽到日期',`is_backup` bit(1) NOT NULL COMMENT '是否補簽',PRIMARY KEY (`id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='簽到記錄表';

這張表中的一條記錄是一個用戶一次的簽到記錄。假如一個用戶1年簽到100次,而網站有100萬用戶,就會產生1億條記錄。隨著用戶量增多、時間的推移,這張表中的數據只會越來越多,占用的空間也會越來越大。

redis bitmap 實現

一個用戶簽到的情況無非就兩種,要么簽了,要么沒。 可以用 0 或者1如果我們按月來統計用戶簽到信息,簽到記錄為1,未簽到則記錄為0,就可以用一個長度為31位的二級制數來表示一個用戶一個月的簽到情況。最終效果如下
image.png

java代碼
引入依賴
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.orchids</groupId><artifactId>signinbybitmap</artifactId><version>0.0.1-SNAPSHOT</version><name>signinbybitmap</name><description>signinbybitmap</description><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.6.13</spring-boot.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>3.0.3</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version><configuration><mainClass>com.orchids.signinbybitmap.SignByBitmapApplication</mainClass><skip>true</skip></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>
配置文件

# 應用服務 WEB 訪問端口
server:port: 8080spring:redis:host: localhostport: 6379password: 6379mvc:pathmatch:matching-strategy: ant_path_matcher

knife4j配置類

package com.orchids.signinbybitmap.web.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
//import springfox.documentation.swagger2.annotations.EnableSwagger2;/*** @ Author qwh* @ Date 2024/7/5 13:08*/
@Configuration
//@EnableSwagger2
public class knife4jConfiguration {@Beanpublic Docket webApiConfig(){// 創建Docket實例Docket webApi = new Docket(DocumentationType.SWAGGER_2).groupName("StudentApi").apiInfo(webApiInfo()).select()// 選擇需要文檔化的API,只顯示指定包下的頁面.apis(RequestHandlerSelectors.basePackage("com.orchids.signinbybitmap"))// 指定路徑匹配規則,只對/student開頭的路徑進行文檔化.paths(PathSelectors.regex("/User/.*")).build();return webApi;}/*** 構建API信息* 本函數用于創建并返回一個ApiInfo對象,該對象包含了API文檔的標題、描述、版本以及聯系方式等信息。* @return 返回構建好的ApiInfo對象*/private ApiInfo webApiInfo(){// 使用ApiInfoBuilder構建API信息return new ApiInfoBuilder().title("Student message API文檔") // 設置文檔標題.description("本文檔描述了Swagger2測試接口定義") // 設置文檔描述.version("1.0") // 設置文檔版本號.contact(new Contact("nullpointer", "http://blog.nullpointer.love", "nullpointer2024@gmail.com")) // 設置聯系人信息.build(); // 構建并返回ApiInfo對象}
}
controller
package com.orchids.signinbybitmap.web.controller;import com.orchids.signinbybitmap.web.domain.result.Result;
import com.orchids.signinbybitmap.web.domain.vo.SignResultVO;
import com.orchids.signinbybitmap.web.service.SignService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;/*** @ Author qwh* @ Date 2024/7/5 13:01*/
@Api(tags = "簽到相關接口")
@RestController
@RequestMapping("/User")
@RequiredArgsConstructor
public class SignController {private final SignService signService;@ApiOperation("簽到")@GetMapping("Sign")public Result<SignResultVO> AddSignRecords() {return signService.AddSignRecords();}
}
service
package com.orchids.signinbybitmap.web.service;import com.orchids.signinbybitmap.web.domain.result.Result;
import com.orchids.signinbybitmap.web.domain.vo.SignResultVO;/*** @ Author qwh* @ Date 2024/7/5 13:35*/
public interface SignService {Result<SignResultVO> AddSignRecords();
}

可以擴展其他功能

package com.orchids.signinbybitmap.web.service.impl;import com.orchids.signinbybitmap.web.domain.result.Result;
import com.orchids.signinbybitmap.web.domain.vo.SignResultVO;
import com.orchids.signinbybitmap.web.exception.SignException;
import com.orchids.signinbybitmap.web.service.SignService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.LinkedList;
import java.util.List;/*** @ Author qwh* @ Date 2024/7/5 13:35*/
@Slf4j
@Service
@RequiredArgsConstructor
public class SignServiceImpl implements SignService {private final String SIGN_UID= "sign:uid:";private final StringRedisTemplate redisTemplate;@Overridepublic Result<SignResultVO> AddSignRecords() {SignResultVO vo = new SignResultVO();//獲取簽到用戶Long userId = 1388888L;//獲取簽到日期LocalDateTime now = LocalDateTime.now();String format = now.format(DateTimeFormatter.ofPattern(":yyyy-MM-dd"));//設置redisKey   sign:uid:1388888:2024-07-05 5 1String key = SIGN_UID + userId.toString() + format;//計算簽到偏移量int offset = now.getDayOfMonth() - 1;//添加簽到記錄到redisBoolean sign = redisTemplate.opsForValue().setBit(key, offset, true);if (sign){throw new SignException("親!您今天已經登錄過喲 (?′?`?)",520);}//計算連續簽到天數int day = now.getDayOfMonth();int continueDays = countSignDays(key,day);int rewardPoints = 0;switch (continueDays){case 2:rewardPoints = 10;break;case 4:rewardPoints=20;break;case 6:rewardPoints = 40;break;}//獲取簽到詳情信息List<Integer> signDayRecord = SignRecords(userId,key,day);vo.setUserId(userId.intValue());vo.setSignDays(continueDays);vo.setRewardPoints(rewardPoints);vo.setSignRecords(signDayRecord);return Result.ok(vo);}/*** 獲取連續簽到天數* @param key* @param days* @return*/private int countSignDays(String key, int days) {//從redis讀取簽到記錄List<Long> nums = redisTemplate.opsForValue().bitField(key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(days)).valueAt(0));//計算簽到次數int num = nums.get(0).intValue();//num與1進行與計算得到二進制的末尾 當末尾為1 說明簽到 為0 說明沒有簽到int result = 0;while ((num & 1) == 1) {result++;num = num >>>1;}//返回簽到結果return result;}/*** 獲取簽到詳情* @param userId* @param key* @param day* @return*/private List<Integer> SignRecords(Long userId, String key, int day) {//獲取從redis中獲取登錄信息List<Long> sign = redisTemplate.opsForValue().bitField(key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(day)).valueAt(0));int num = sign.get(0).intValue();LinkedList<Integer> result = new LinkedList<>();while (day > 0) {result.addFirst(num & 1);num = num >>> 1;day--;}return result;}
}
其他類
package com.orchids.signinbybitmap.web.domain.result;import lombok.Data;/*** @ Author qwh* @ Date 2024/7/5 16:52*/
@Data
public class Result<T> {//返回碼private Integer code;//返回消息private String message;//返回數據private T data;public Result() {}private static <T> Result<T> build(T data) {Result<T> result = new Result<>();if (data != null)result.setData(data);return result;}public static <T> Result<T> build(T body, ResultCode resultCode) {Result<T> result = build(body);result.setCode(resultCode.getCode());result.setMessage(resultCode.getMessage());return result;}public static <T> Result<T> ok(T data) {return build(data, ResultCode.SUCCESS);}public static <T> Result<T> ok() {return Result.ok(null);}public static <T> Result<T> fail(Integer code, String message) {Result<T> result = build(null);result.setCode(code);result.setMessage(message);return result;}public static <T> Result<T> fail() {return build(null, ResultCode.FAIL);}
}
package com.orchids.signinbybitmap.web.domain.result;import lombok.Getter;/*** @ Author qwh* @ Date 2024/7/5 16:54*/
@Getter
public enum ResultCode {SUCCESS(200, "成功"),FAIL(201, "失敗"),PARAM_ERROR(202, "參數不正確"),SERVICE_ERROR(203, "服務異常"),DATA_ERROR(204, "數據異常"),ILLEGAL_REQUEST(205, "非法請求"),REPEAT_SUBMIT(206, "重復提交"),DELETE_ERROR(207, "請先刪除子集"),ADMIN_ACCOUNT_EXIST_ERROR(301, "賬號已存在"),ADMIN_CAPTCHA_CODE_ERROR(302, "驗證碼錯誤"),ADMIN_CAPTCHA_CODE_EXPIRED(303, "驗證碼已過期"),ADMIN_CAPTCHA_CODE_NOT_FOUND(304, "未輸入驗證碼"),ADMIN_ACCOUNT_NOT_EXIST(330,"用戶不存在"),ADMIN_LOGIN_AUTH(305, "未登陸"),ADMIN_ACCOUNT_NOT_EXIST_ERROR(306, "賬號不存在"),ADMIN_ACCOUNT_ERROR(307, "用戶名或密碼錯誤"),ADMIN_ACCOUNT_DISABLED_ERROR(308, "該用戶已被禁用"),ADMIN_ACCESS_FORBIDDEN(309, "無訪問權限"),APP_LOGIN_AUTH(501, "未登陸"),APP_LOGIN_PHONE_EMPTY(502, "手機號碼為空"),APP_LOGIN_CODE_EMPTY(503, "驗證碼為空"),APP_SEND_SMS_TOO_OFTEN(504, "驗證法發送過于頻繁"),APP_LOGIN_CODE_EXPIRED(505, "驗證碼已過期"),APP_LOGIN_CODE_ERROR(506, "驗證碼錯誤"),APP_ACCOUNT_DISABLED_ERROR(507, "該用戶已被禁用"),TOKEN_EXPIRED(601, "token過期"),TOKEN_INVALID(602, "token非法");private final Integer code;private final String message;ResultCode(Integer code, String message) {this.code = code;this.message = message;}
}
package com.orchids.signinbybitmap.web.domain.vo;import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.models.auth.In;
import lombok.Data;import java.util.List;/*** @ Author qwh* @ Date 2024/7/5 13:36*/
@Data
@ApiModel(description = "簽到結果")
public class SignResultVO {@ApiModelProperty("簽到人")private Integer UserId;@ApiModelProperty("簽到得分")private Integer signPoints = 1;@ApiModelProperty("連續簽到天數")private Integer signDays;@ApiModelProperty("連續簽到獎勵積分,連續簽到超過7天以上才有獎勵")private Integer rewardPoints;@ApiModelProperty("簽到詳細信息")private List<Integer> signRecords;}
package com.orchids.signinbybitmap.web.exception;import com.orchids.signinbybitmap.web.domain.result.Result;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;/*** @ Author qwh* @ Date 2024/7/5 16:51*/
@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)@ResponseBodypublic Result error(Exception e){e.printStackTrace();return Result.fail();}@ExceptionHandler(SignException.class)@ResponseBodypublic Result error(SignException e){e.printStackTrace();return Result.fail(e.getCode(), e.getMessage());}
}
package com.orchids.signinbybitmap.web.exception;import lombok.Data;/*** @ Author qwh* @ Date 2024/7/5 16:47*/
@Data
public class SignException extends RuntimeException{//異常狀態碼private Integer code;/*** 通過狀態碼和錯誤消息創建異常對象* @param message* @param code*/public SignException(String message, Integer code) {super(message);this.code = code;}@Overridepublic String toString() {return "SignException{" +"code=" + code +", message=" + this.getMessage() +'}';}
}
package com.orchids.signinbybitmap;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class SignByBitmapApplication {public static void main(String[] args) {SpringApplication.run(SignByBitmapApplication.class, args);}}
測試結果

image.png

image.png

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/40376.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/40376.shtml
英文地址,請注明出處:http://en.pswp.cn/web/40376.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

EI檢索被認為是工程技術領域的權威數據庫

EI檢索被認為是工程技術領域的權威數據庫&#xff0c;能夠被EI檢索收錄的期刊和會議論文通常被認為具有一定的學術質量和影響力。然而&#xff0c;EI檢索與“高水平”不能完全畫等號&#xff0c;以下是一些需要考慮的因素&#xff1a; 1. 收錄標準&#xff1a;雖然EI檢索有嚴格…

在Linux操作系統中關于邏輯卷的案例

1.如何去創建一個邏輯卷 1.1先去創建物理卷 如上圖所示&#xff0c;physical volume 物理卷 被成功創建。 如上圖所示&#xff0c;可以使用pvscan來去查看當前Linux操作系統的物理卷/ 1.2使用創建好的物理卷去創建一個卷組。 如上圖所示&#xff0c;可以使用第一步創建的兩個…

【中項第三版】系統集成項目管理工程師 | 第 9 章 項目管理概論③ | 9.6 - 9.10

前言 第 9 章對應的內容選擇題和案例分析都會進行考查&#xff0c;這一章節理論性較強&#xff0c;學習要以教材為準。本章分值預計在4-5分。 目錄 9.6 項目管理過程組 9.7 項目管理原則 9.8 項目管理知識領域 9.9 價值交付系統 9.10 本章練習 9.6 項目管理過程組 項目…

千萬不能踏入的渠道管理五大誤區!【附策略】

一、引言 在當今激烈的市場競爭環境中&#xff0c;有效的渠道管理是企業獲得競爭優勢的關鍵。然而&#xff0c;在實踐過程中&#xff0c;不少企業因陷入管理誤區而影響了市場拓展和品牌建設。本文旨在揭示渠道管理中常見的五大誤區&#xff0c;并提供避免策略&#xff0c;幫助…

高級Redis之Stream的用法示例

不想自己搭建一個mq怎么辦&#xff1f;Redis的Stream 來幫你&#xff0c;Redis Stream 是 Redis 5.0 引入的一種新的數據結構&#xff0c;用于處理實時的、可持久化的、基于時間序列的數據流。它非常適合處理事件流、日志、消息隊列等場景。下面是一個使用 Redis Stream 的具體…

web基礎與HTTP協議(企業網站架構部署與優化)

補充&#xff1a;http服務首頁文件在/var/www/html下的&#xff0c;一定是index.html命名的文件。才會顯示出來。 如果該路徑下沒有相應的文件&#xff0c;會顯示/usr/share/httpd/noindex下的index.html文件。 如果/usr/share/httpd/noindex沒有index.html文件&#xff0c;會…

BSI 第七屆萬物互聯智慧高峰論壇:主題:擁抱AI時代,標準賦能組織實現可持續發展

BSI 第七屆萬物互聯智慧高峰論壇&#xff1a;主題&#xff1a;擁抱AI時代&#xff0c;標準賦能組織實現可持續發展 主要收到 BSI 溫女士的邀請參加的本次論壇。還是學到的很多 。 在科技日新月異的時代背景下&#xff0c;BSI 第七屆萬物互聯智慧高峰論壇于[時間&#xff1a;6…

Object 類中的公共方法詳解

Object 類中的公共方法詳解 1、clone() 方法2、equals(Object obj) 方法3、hashCode() 方法4、getClass() 方法5、wait() 方法6、notify() 和 notifyAll() 方法 &#x1f496;The Begin&#x1f496;點點關注&#xff0c;收藏不迷路&#x1f496; 在 Java 中&#xff0c;Object…

AI 驅動的數據中心變革與前景

文章主要探討了AI計算時代數據中心的轉型&#xff0c;涉及計算技術的多樣性、規格尺寸和加速器的發展、大型語言模型&#xff08;LLM&#xff09;的發展、功耗和冷卻趨勢、基準測試的重要性以及數據中心的發展等方面。為大家提供深入了解AI基礎設施發展的視角。 計算技術的多樣…

Ubuntu(通用)—網絡加固—ufw+防DNS污染+ARP綁定

1. ufw sudo ufw default deny incoming sudo ufw deny in from any to any # sudo ufw allow from any to any port 5353 protocol udp sudo ufw enable # 啟動開機自啟 # sudo ufw reload 更改后的操作2. 防ARP欺騙 華為云教程 arp -d刪除dns記錄arp -a顯示arp表 ipconfi…

PTrade常見問題系列3

量化允許同時運行回測和交易的策略個數配置。 量化允許同時運行回測和交易的策略個數在哪里查看&#xff1f; 在量化服務器/home/fly/config/custom_config_conf文件中&#xff0c;其中運行回測的策略個數由backtest_switch&#xff08;是否限制普通回測個數&#xff09;及ba…

Qt 日志輸出的選擇方案有多少

Qt 日志輸出的選擇方案主要包括以下幾種&#xff1a; 使用內置的日志函數&#xff1a; qDebug()&#xff1a;用于輸出調試信息。qInfo()&#xff1a;用于輸出一般信息。qWarning()&#xff1a;用于輸出警告信息。qCritical()&#xff1a;用于輸出關鍵錯誤信息&#xff0c;表明…

詳細設計與概要設計區別-慧哥充電樁開源系統

概要設計更側重于系統的整體構架和模塊劃分&#xff0c;而詳細設計則關注具體模塊的實現細節。在軟件開發過程中&#xff0c;這兩個階段雖然緊密相關&#xff0c;但它們各自有著不同的目標和方法。以下是具體分析&#xff1a; 目標 概要設計&#xff1a;概要設計關注系統整體架…

matlab 繪制高等數學中的二維函數示例

matlab 繪制高等數學中的二維函數示例 繪制高等數學中的二維函數示例繪制結果 繪制高等數學中的二維函數示例 clc,clear,close all; % 定義方程 eqn (x, y) (x.^2 y.^2).^3 - y.^4;% 繪制方程曲線和坐標軸 ezplot(eqn, [-2, 2, -2, 2]) hold on % 在同一圖形中保持繪圖% 繪…

S7-1200PLC學習記錄

文章目錄 前言一、S7-12001.數字量輸入模塊2. PNP接法和NPN接法 二、博圖軟件1. 位邏輯運算Part1. 添加新設備&#xff08;添加PLC&#xff09;Part2. 添加信號模塊Part3. 添加信號板中模塊Part4. 添加新塊Part5. Main編程文件案例1案例2 -( S )- 和 -( R )-完整操作過程&#…

昇思25天學習打卡營第8天|ResNet50遷移學習

一、遷移學習定義 遷移學習&#xff08;Transfer Learning&#xff09;&#xff1a;在一個任務上訓練得到的模型包含的知識可以部分或全部地轉移到另一個任務上。允許模型將從一個任務中學到的知識應用到另一個相關的任務中。適用于數據稀缺的情況&#xff0c;可減少對大量標記…

掌握Linux網絡:深入理解TC —— 你的流量控制利器

目錄 簡單概述&#xff1a; qdisc(隊列)&#xff1a; 舉例&#xff1a; Bash 整形隊列算法&#xff1a; FIFO (First-In-First-Out) PFIFO (Priority FIFO) SFQ (Stochastic Fair Queuing) RED (Random Early Detection) HTB (Hierarchical Token Bucket) TBF…

谷粒商城筆記-04-項目微服務架構圖簡介

文章目錄 一&#xff0c;網絡二&#xff0c;網關1&#xff0c;網關選型2&#xff0c;認證鑒權3&#xff0c;動態路由4&#xff0c;限流5&#xff0c;負載均衡6&#xff0c;熔斷降級 三&#xff0c;微服務四&#xff0c;存儲層五&#xff0c;服務治理六&#xff0c;日志系統七&a…

前端面試題3-淺談http協議及常見的面試題

1、淺談http協議 HTTP&#xff08;Hypertext Transfer Protocol&#xff09;超文本傳輸協議&#xff0c;是互聯網上應用最為廣泛的一種網絡協議&#xff0c;所有的WWW文件都必須遵守這個標準。它是基于TCP/IP通信協議來傳遞數據&#xff08;HTML文件、圖片文件、查詢結果等&am…

在Apache HTTP服務器上配置 TLS加密

安裝mod_ssl軟件包 [rootlocalhost conf.d]# dnf install mod_ssl -y此時查看監聽端口多了一個443端口 自己構造證書 [rootlocalhost conf.d]# cd /etc/pki/tls/certs/ [rootlocalhost certs]# openssl genrsa > jiami.key [rootlocalhost certs]# openssl req -utf8 -n…